swagger2 0.1 → 0.2
raw patch · 7 files changed
+1954/−742 lines, 7 filesdep +lens
Dependencies added: lens
Files
- CHANGELOG.md +8/−0
- src/Data/Swagger.hs +4/−0
- src/Data/Swagger/Internal.hs +173/−132
- src/Data/Swagger/Internal/Utils.hs +1/−1
- src/Data/Swagger/Lens.hs +107/−0
- swagger2.cabal +6/−1
- test/Data/SwaggerSpec.hs +1655/−608
+ CHANGELOG.md view
@@ -0,0 +1,8 @@+0.2+---+* Add `Data.Swagger.Lens`+* Support references+* Fixes:+ * Fix `FromJSON SwaggerHost` instance+ * Add missing `Maybe`s for field types+ * Decode petstore swagger.json successfully
src/Data/Swagger.hs view
@@ -70,6 +70,10 @@ -- * External documentation SwaggerExternalDocs(..), + -- * References+ SwaggerReference(..),+ SwaggerReferenced(..),+ -- * Miscellaneous SwaggerMimeList(..), URL(..),
src/Data/Swagger/Internal.hs view
@@ -35,61 +35,61 @@ data Swagger = Swagger { -- | Provides metadata about the API. -- The metadata can be used by the clients if needed.- swaggerInfo :: SwaggerInfo+ _swaggerInfo :: SwaggerInfo -- | The host (name or ip) serving the API. It MAY include a port. -- If the host is not included, the host serving the documentation is to be used (including the port).- , swaggerHost :: Maybe SwaggerHost+ , _swaggerHost :: Maybe SwaggerHost -- | The base path on which the API is served, which is relative to the host. -- If it is not included, the API is served directly under the host. -- The value MUST start with a leading slash (/).- , swaggerBasePath :: Maybe FilePath+ , _swaggerBasePath :: Maybe FilePath -- | The transfer protocol of the API. -- If the schemes is not included, the default scheme to be used is the one used to access the Swagger definition itself.- , swaggerSchemes :: Maybe [SwaggerScheme]+ , _swaggerSchemes :: Maybe [SwaggerScheme] -- | A list of MIME types the APIs can consume. -- This is global to all APIs but can be overridden on specific API calls.- , swaggerConsumes :: SwaggerMimeList+ , _swaggerConsumes :: SwaggerMimeList -- | A list of MIME types the APIs can produce. -- This is global to all APIs but can be overridden on specific API calls. - , swaggerProduces :: SwaggerMimeList+ , _swaggerProduces :: SwaggerMimeList -- | The available paths and operations for the API.- , swaggerPaths :: SwaggerPaths+ , _swaggerPaths :: SwaggerPaths -- | An object to hold data types produced and consumed by operations.- , swaggerDefinitions :: HashMap Text SwaggerSchema+ , _swaggerDefinitions :: HashMap Text SwaggerSchema -- | An object to hold parameters that can be used across operations. -- This property does not define global parameters for all operations.- , swaggerParameters :: HashMap Text SwaggerParameter+ , _swaggerParameters :: HashMap Text SwaggerParameter -- | An object to hold responses that can be used across operations. -- This property does not define global responses for all operations.- , swaggerResponses :: HashMap Text SwaggerResponse+ , _swaggerResponses :: HashMap Text SwaggerResponse -- | Security scheme definitions that can be used across the specification.- , swaggerSecurityDefinitions :: HashMap Text SwaggerSecurityScheme+ , _swaggerSecurityDefinitions :: HashMap Text SwaggerSecurityScheme -- | A declaration of which security schemes are applied for the API as a whole. -- The list of values describes alternative security schemes that can be used -- (that is, there is a logical OR between the security requirements). -- Individual operations can override this definition.- , swaggerSecurity :: [SwaggerSecurityRequirement]+ , _swaggerSecurity :: [SwaggerSecurityRequirement] -- | A list of tags used by the specification with additional metadata. -- The order of the tags can be used to reflect on their order by the parsing tools. -- Not all tags that are used by the Operation Object must be declared. -- The tags that are not declared may be organized randomly or based on the tools' logic. -- Each tag name in the list MUST be unique.- , swaggerTags :: [SwaggerTag]+ , _swaggerTags :: [SwaggerTag] -- | Additional external documentation.- , swaggerExternalDocs :: Maybe SwaggerExternalDocs+ , _swaggerExternalDocs :: Maybe SwaggerExternalDocs } deriving (Eq, Show, Generic) -- | The object provides metadata about the API.@@ -97,51 +97,51 @@ -- and can be presented in the Swagger-UI for convenience. data SwaggerInfo = SwaggerInfo { -- | The title of the application.- swaggerInfoTitle :: Text+ _swaggerInfoTitle :: Text -- | A short description of the application. -- GFM syntax can be used for rich text representation.- , swaggerInfoDescription :: Maybe Text+ , _swaggerInfoDescription :: Maybe Text -- | The Terms of Service for the API.- , swaggerInfoTermsOfService :: Maybe Text+ , _swaggerInfoTermsOfService :: Maybe Text -- | The contact information for the exposed API.- , swaggerInfoContact :: Maybe SwaggerContact+ , _swaggerInfoContact :: Maybe SwaggerContact -- | The license information for the exposed API.- , swaggerInfoLicense :: Maybe SwaggerLicense+ , _swaggerInfoLicense :: Maybe SwaggerLicense -- | Provides the version of the application API -- (not to be confused with the specification version).- , swaggerInfoVersion :: Text+ , _swaggerInfoVersion :: Text } deriving (Eq, Show, Generic) -- | Contact information for the exposed API. data SwaggerContact = SwaggerContact { -- | The identifying name of the contact person/organization.- swaggerContactName :: Maybe Text+ _swaggerContactName :: Maybe Text -- | The URL pointing to the contact information.- , swaggerContactUrl :: Maybe URL+ , _swaggerContactUrl :: Maybe URL -- | The email address of the contact person/organization.- , swaggerContactEmail :: Maybe Text+ , _swaggerContactEmail :: Maybe Text } deriving (Eq, Show) -- | License information for the exposed API. data SwaggerLicense = SwaggerLicense { -- | The license name used for the API.- swaggerLicenseName :: Text+ _swaggerLicenseName :: Text -- | A URL to the license used for the API.- , swaggerLicenseUrl :: Maybe URL+ , _swaggerLicenseUrl :: Maybe URL } deriving (Eq, Show) -- | The host (name or ip) serving the API. It MAY include a port. data SwaggerHost = SwaggerHost- { swaggerHostName :: HostName -- ^ Host name.- , swaggerHostPort :: Maybe PortNumber -- ^ Optional port.+ { _swaggerHostName :: HostName -- ^ Host name.+ , _swaggerHostPort :: Maybe PortNumber -- ^ Optional port. } deriving (Eq, Show) -- | The transfer protocol of the API.@@ -156,7 +156,7 @@ data SwaggerPaths = SwaggerPaths { -- | Holds the relative paths to the individual endpoints. -- The path is appended to the @'swaggerBasePath'@ in order to construct the full URL.- swaggerPathsMap :: HashMap FilePath SwaggerPathItem+ _swaggerPathsMap :: HashMap FilePath SwaggerPathItem } deriving (Eq, Show, Generic) -- | Describes the operations available on a single path.@@ -165,91 +165,91 @@ -- but they will not know which operations and parameters are available. data SwaggerPathItem = SwaggerPathItem { -- | A definition of a GET operation on this path.- swaggerPathItemGet :: Maybe SwaggerOperation+ _swaggerPathItemGet :: Maybe SwaggerOperation -- | A definition of a PUT operation on this path.- , swaggerPathItemPut :: Maybe SwaggerOperation+ , _swaggerPathItemPut :: Maybe SwaggerOperation -- | A definition of a POST operation on this path.- , swaggerPathItemPost :: Maybe SwaggerOperation+ , _swaggerPathItemPost :: Maybe SwaggerOperation -- | A definition of a DELETE operation on this path.- , swaggerPathItemDelete :: Maybe SwaggerOperation+ , _swaggerPathItemDelete :: Maybe SwaggerOperation -- | A definition of a OPTIONS operation on this path.- , swaggerPathItemOptions :: Maybe SwaggerOperation+ , _swaggerPathItemOptions :: Maybe SwaggerOperation -- | A definition of a HEAD operation on this path.- , swaggerPathItemHead :: Maybe SwaggerOperation+ , _swaggerPathItemHead :: Maybe SwaggerOperation -- | A definition of a PATCH operation on this path.- , swaggerPathItemPatch :: Maybe SwaggerOperation+ , _swaggerPathItemPatch :: Maybe SwaggerOperation -- | A list of parameters that are applicable for all the operations described under this path. -- These parameters can be overridden at the operation level, but cannot be removed there. -- The list MUST NOT include duplicated parameters. -- A unique parameter is defined by a combination of a name and location.- , swaggerPathItemParameters :: [SwaggerParameter]+ , _swaggerPathItemParameters :: [SwaggerReferenced SwaggerParameter] } deriving (Eq, Show, Generic) -- | Describes a single API operation on a path. data SwaggerOperation = SwaggerOperation { -- | A list of tags for API documentation control. -- Tags can be used for logical grouping of operations by resources or any other qualifier.- swaggerOperationTags :: [Text]+ _swaggerOperationTags :: [Text] -- | A short summary of what the operation does. -- For maximum readability in the swagger-ui, this field SHOULD be less than 120 characters.- , swaggerOperationSummary :: Maybe Text+ , _swaggerOperationSummary :: Maybe Text -- | A verbose explanation of the operation behavior. -- GFM syntax can be used for rich text representation.- , swaggerOperationDescription :: Maybe Text+ , _swaggerOperationDescription :: Maybe Text -- | Additional external documentation for this operation.- , swaggerOperationExternalDocs :: Maybe SwaggerExternalDocs+ , _swaggerOperationExternalDocs :: Maybe SwaggerExternalDocs -- | Unique string used to identify the operation. -- The id MUST be unique among all operations described in the API. -- Tools and libraries MAY use the it to uniquely identify an operation, -- therefore, it is recommended to follow common programming naming conventions.- , swaggerOperationOperationId :: Maybe Text+ , _swaggerOperationOperationId :: Maybe Text -- | A list of MIME types the operation can consume. -- This overrides the @'swaggerConsumes'@. -- @Just []@ MAY be used to clear the global definition.- , swaggerOperationConsumes :: Maybe SwaggerMimeList+ , _swaggerOperationConsumes :: Maybe SwaggerMimeList -- | A list of MIME types the operation can produce. -- This overrides the @'swaggerProduces'@. -- @Just []@ MAY be used to clear the global definition.- , swaggerOperationProduces :: Maybe SwaggerMimeList+ , _swaggerOperationProduces :: Maybe SwaggerMimeList -- | A list of parameters that are applicable for this operation. -- If a parameter is already defined at the @'SwaggerPathItem'@, -- the new definition will override it, but can never remove it. -- The list MUST NOT include duplicated parameters. -- A unique parameter is defined by a combination of a name and location.- , swaggerOperationParameters :: [SwaggerParameter]+ , _swaggerOperationParameters :: [SwaggerReferenced SwaggerParameter] -- | The list of possible responses as they are returned from executing this operation.- , swaggerOperationResponses :: SwaggerResponses+ , _swaggerOperationResponses :: SwaggerResponses -- | The transfer protocol for the operation. -- The value overrides @'swaggerSchemes'@.- , swaggerOperationSchemes :: Maybe [SwaggerScheme]+ , _swaggerOperationSchemes :: Maybe [SwaggerScheme] -- | Declares this operation to be deprecated. -- Usage of the declared operation should be refrained. -- Default value is @False@.- , swaggerOperationDeprecated :: Bool+ , _swaggerOperationDeprecated :: Bool -- | A declaration of which security schemes are applied for this operation. -- The list of values describes alternative security schemes that can be used -- (that is, there is a logical OR between the security requirements). -- This definition overrides any declared top-level security. -- To remove a top-level security declaration, @Just []@ can be used.- , swaggerOperationSecurity :: [SwaggerSecurityRequirement]+ , _swaggerOperationSecurity :: [SwaggerSecurityRequirement] } deriving (Eq, Show, Generic) newtype SwaggerMimeList = SwaggerMimeList { getSwaggerMimeList :: [MediaType] }@@ -260,30 +260,30 @@ data SwaggerParameter = SwaggerParameter { -- | The name of the parameter. -- Parameter names are case sensitive.- swaggerParameterName :: Text+ _swaggerParameterName :: Text -- | A brief description of the parameter. -- This could contain examples of use. -- GFM syntax can be used for rich text representation.- , swaggerParameterDescription :: Maybe Text+ , _swaggerParameterDescription :: Maybe Text -- | Determines whether this parameter is mandatory. -- If the parameter is in "path", this property is required and its value MUST be true. -- Otherwise, the property MAY be included and its default value is @False@.- , swaggerParameterRequired :: Bool+ , _swaggerParameterRequired :: Bool -- | Parameter schema.- , swaggerParameterSchema :: SwaggerParameterSchema+ , _swaggerParameterSchema :: SwaggerParameterSchema } deriving (Eq, Show, Generic) data SwaggerParameterSchema- = SwaggerParameterBody SwaggerSchema+ = SwaggerParameterBody (SwaggerReferenced SwaggerSchema) | SwaggerParameterOther SwaggerParameterOtherSchema deriving (Eq, Show) data SwaggerParameterOtherSchema = SwaggerParameterOtherSchema { -- | The location of the parameter.- swaggerParameterOtherSchemaIn :: SwaggerParameterLocation+ _swaggerParameterOtherSchemaIn :: SwaggerParameterLocation -- | The type of the parameter. -- Since the parameter is not located at the request body,@@ -291,26 +291,26 @@ -- If type is @'SwaggerParamFile'@, the @consumes@ MUST be either -- "multipart/form-data" or " application/x-www-form-urlencoded" -- and the parameter MUST be in @'SwaggerParameterFormData'@.- , swaggerParameterOtherSchemaType :: SwaggerParameterType+ , _swaggerParameterOtherSchemaType :: SwaggerParameterType -- | The extending format for the previously mentioned type.- , swaggerParameterOtherSchemaFormat :: Maybe SwaggerFormat+ , _swaggerParameterOtherSchemaFormat :: Maybe SwaggerFormat -- | Sets the ability to pass empty-valued parameters. -- This is valid only for either @'SwaggerParameterQuery'@ or @'SwaggerParameterFormData'@ -- and allows you to send a parameter with a name only or an empty value. -- Default value is @False@.- , swaggerParameterOtherSchemaAllowEmptyValue :: Bool+ , _swaggerParameterOtherSchemaAllowEmptyValue :: Bool -- | __Required if type is @'SwaggerParamArray'@__. -- Describes the type of items in the array.- , swaggerParameterOtherSchemaItems :: Maybe SwaggerItems+ , _swaggerParameterOtherSchemaItems :: Maybe SwaggerItems -- | Determines the format of the array if @'SwaggerParamArray'@ is used. -- Default value is csv.- , swaggerParameterOtherSchemaCollectionFormat :: Maybe SwaggerCollectionFormat+ , _swaggerParameterOtherSchemaCollectionFormat :: Maybe SwaggerCollectionFormat - , swaggerParameterOtherSchemaCommon :: SwaggerSchemaCommon+ , _swaggerParameterOtherSchemaCommon :: SwaggerSchemaCommon } deriving (Eq, Show, Generic) data SwaggerParameterType@@ -384,32 +384,32 @@ type SwaggerParamName = Text data SwaggerSchema = SwaggerSchema- { swaggerSchemaType :: SwaggerSchemaType- , swaggerSchemaFormat :: Maybe SwaggerFormat- , swaggerSchemaTitle :: Maybe Text- , swaggerSchemaDescription :: Maybe Text- , swaggerSchemaRequired :: [SwaggerParamName]+ { _swaggerSchemaType :: SwaggerSchemaType+ , _swaggerSchemaFormat :: Maybe SwaggerFormat+ , _swaggerSchemaTitle :: Maybe Text+ , _swaggerSchemaDescription :: Maybe Text+ , _swaggerSchemaRequired :: [SwaggerParamName] - , swaggerSchemaItems :: Maybe SwaggerSchemaItems- , swaggerSchemaAllOf :: Maybe [SwaggerSchema]- , swaggerSchemaProperties :: HashMap Text SwaggerSchema- , swaggerSchemaAdditionalProperties :: Maybe SwaggerSchema+ , _swaggerSchemaItems :: Maybe SwaggerSchemaItems+ , _swaggerSchemaAllOf :: Maybe [SwaggerSchema]+ , _swaggerSchemaProperties :: HashMap Text (SwaggerReferenced SwaggerSchema)+ , _swaggerSchemaAdditionalProperties :: Maybe SwaggerSchema - , swaggerSchemaDiscriminator :: Maybe Text- , swaggerSchemaReadOnly :: Maybe Bool- , swaggerSchemaXml :: Maybe SwaggerXml- , swaggerSchemaExternalDocs :: Maybe SwaggerExternalDocs- , swaggerSchemaExample :: Maybe Value+ , _swaggerSchemaDiscriminator :: Maybe Text+ , _swaggerSchemaReadOnly :: Maybe Bool+ , _swaggerSchemaXml :: Maybe SwaggerXml+ , _swaggerSchemaExternalDocs :: Maybe SwaggerExternalDocs+ , _swaggerSchemaExample :: Maybe Value - , swaggerSchemaMaxProperties :: Maybe Integer- , swaggerSchemaMinProperties :: Maybe Integer+ , _swaggerSchemaMaxProperties :: Maybe Integer+ , _swaggerSchemaMinProperties :: Maybe Integer - , swaggerSchemaCommon :: SwaggerSchemaCommon+ , _swaggerSchemaCommon :: SwaggerSchemaCommon } deriving (Eq, Show, Generic) data SwaggerSchemaItems- = SwaggerSchemaItemsObject SwaggerSchema- | SwaggerSchemaItemsArray [SwaggerSchema]+ = SwaggerSchemaItemsObject (SwaggerReferenced SwaggerSchema)+ | SwaggerSchemaItemsArray [SwaggerReferenced SwaggerSchema] deriving (Eq, Show) data SwaggerSchemaCommon = SwaggerSchemaCommon@@ -418,20 +418,20 @@ -- if not supplied by the client in the request. -- (Note: "default" has no meaning for required parameters.) -- Unlike JSON Schema this value MUST conform to the defined type for this parameter.- swaggerSchemaDefault :: Maybe Value+ _swaggerSchemaDefault :: Maybe Value - , swaggerSchemaMaximum :: Maybe Integer- , swaggerSchemaExclusiveMaximum :: Maybe Bool- , swaggerSchemaMinimum :: Maybe Integer- , swaggerSchemaExclusiveMinimum :: Maybe Bool- , swaggerSchemaMaxLength :: Maybe Integer- , swaggerSchemaMinLength :: Maybe Integer- , swaggerSchemaPattern :: Maybe Text- , swaggerSchemaMaxItems :: Maybe Integer- , swaggerSchemaMinItems :: Maybe Integer- , swaggerSchemaUniqueItems :: Maybe Bool- , swaggerSchemaEnum :: Maybe [Value]- , swaggerSchemaMultipleOf :: Maybe Integer+ , _swaggerSchemaMaximum :: Maybe Integer+ , _swaggerSchemaExclusiveMaximum :: Maybe Bool+ , _swaggerSchemaMinimum :: Maybe Integer+ , _swaggerSchemaExclusiveMinimum :: Maybe Bool+ , _swaggerSchemaMaxLength :: Maybe Integer+ , _swaggerSchemaMinLength :: Maybe Integer+ , _swaggerSchemaPattern :: Maybe Text+ , _swaggerSchemaMaxItems :: Maybe Integer+ , _swaggerSchemaMinItems :: Maybe Integer+ , _swaggerSchemaUniqueItems :: Maybe Bool+ , _swaggerSchemaEnum :: Maybe [Value]+ , _swaggerSchemaMultipleOf :: Maybe Integer } deriving (Eq, Show, Generic) data SwaggerXml = SwaggerXml@@ -440,18 +440,18 @@ -- When defined alongside type being array (outside the items), -- it will affect the wrapping element and only if wrapped is true. -- If wrapped is false, it will be ignored.- swaggerXmlName :: Maybe Text+ _swaggerXmlName :: Maybe Text -- | The URL of the namespace definition. -- Value SHOULD be in the form of a URL.- , swaggerXmlNamespace :: Maybe Text+ , _swaggerXmlNamespace :: Maybe Text -- | The prefix to be used for the name.- , swaggerXmlPrefix :: Maybe Text+ , _swaggerXmlPrefix :: Maybe Text -- | Declares whether the property definition translates to an attribute instead of an element. -- Default value is @False@.- , swaggerXmlAttribute :: Bool+ , _swaggerXmlAttribute :: Bool -- | MAY be used only for an array definition. -- Signifies whether the array is wrapped@@ -459,25 +459,25 @@ -- or unwrapped (@\<book/\>\<book/\>@). -- Default value is @False@. -- The definition takes effect only when defined alongside type being array (outside the items).- , swaggerXmlWrapped :: Bool- } deriving (Eq, Show)+ , _swaggerXmlWrapped :: Bool+ } deriving (Eq, Show, Generic) data SwaggerItems = SwaggerItems { -- | The internal type of the array.- swaggerItemsType :: SwaggerItemsType+ _swaggerItemsType :: SwaggerItemsType -- | The extending format for the previously mentioned type.- , swaggerItemsFormat :: SwaggerFormat+ , _swaggerItemsFormat :: Maybe SwaggerFormat -- | __Required if type is @'SwaggerItemsArray'@.__ -- Describes the type of items in the array.- , swaggerItemsItems :: SwaggerItems+ , _swaggerItemsItems :: Maybe SwaggerItems -- | Determines the format of the array if type array is used. -- Default value is @'SwaggerItemsCollectionCSV'@.- , swaggerItemsCollectionFormat :: SwaggerItemsCollectionFormat+ , _swaggerItemsCollectionFormat :: Maybe SwaggerItemsCollectionFormat - , swaggerItemsCommon :: SwaggerSchemaCommon+ , _swaggerItemsCommon :: SwaggerSchemaCommon } deriving (Eq, Show, Generic) -- | A container for the expected responses of an operation.@@ -488,11 +488,11 @@ data SwaggerResponses = SwaggerResponses { -- | The documentation of responses other than the ones declared for specific HTTP response codes. -- It can be used to cover undeclared responses.- swaggerResponsesDefault :: Maybe SwaggerResponse+ _swaggerResponsesDefault :: Maybe (SwaggerReferenced SwaggerResponse) -- | Any HTTP status code can be used as the property name (one property per HTTP status code). -- Describes the expected response for those HTTP status codes.- , swaggerResponsesResponses :: HashMap HttpStatusCode SwaggerResponse+ , _swaggerResponsesResponses :: HashMap HttpStatusCode (SwaggerReferenced SwaggerResponse) } deriving (Eq, Show, Generic) type HttpStatusCode = Int@@ -501,43 +501,43 @@ data SwaggerResponse = SwaggerResponse { -- | A short description of the response. -- GFM syntax can be used for rich text representation.- swaggerResponseDescription :: Text+ _swaggerResponseDescription :: Text -- | A definition of the response structure. -- It can be a primitive, an array or an object. -- If this field does not exist, it means no content is returned as part of the response. -- As an extension to the Schema Object, its root type value may also be "file". -- This SHOULD be accompanied by a relevant produces mime-type.- , swaggerResponseSchema :: Maybe SwaggerSchema+ , _swaggerResponseSchema :: Maybe (SwaggerReferenced SwaggerSchema) -- | A list of headers that are sent with the response.- , swaggerResponseHeaders :: HashMap HeaderName SwaggerHeader+ , _swaggerResponseHeaders :: HashMap HeaderName SwaggerHeader -- | An example of the response message.- , swaggerResponseExamples :: Maybe SwaggerExample+ , _swaggerResponseExamples :: Maybe SwaggerExample } deriving (Eq, Show, Generic) type HeaderName = Text data SwaggerHeader = SwaggerHeader { -- | A short description of the header.- swaggerHeaderDescription :: Maybe String+ _swaggerHeaderDescription :: Maybe Text -- | The type of the object.- , swaggerHeaderType :: SwaggerItemsType+ , _swaggerHeaderType :: SwaggerItemsType -- | The extending format for the previously mentioned type. See Data Type Formats for further details.- , swaggerHeaderFormat :: Maybe SwaggerFormat+ , _swaggerHeaderFormat :: Maybe SwaggerFormat -- | __Required if type is @'SwaggerItemsArray'@__. -- Describes the type of items in the array.- , swaggerHeaderItems :: SwaggerItems+ , _swaggerHeaderItems :: Maybe SwaggerItems -- | Determines the format of the array if type array is used. -- Default value is @'SwaggerItemsCollectionCSV'@.- , swaggerHeaderCollectionFormat :: SwaggerItemsCollectionFormat+ , _swaggerHeaderCollectionFormat :: Maybe SwaggerItemsCollectionFormat - , swaggerHeaderCommon :: SwaggerSchemaCommon+ , _swaggerHeaderCommon :: SwaggerSchemaCommon } deriving (Eq, Show, Generic) data SwaggerExample = SwaggerExample { getSwaggerExample :: Map MediaType Value }@@ -551,10 +551,10 @@ data SwaggerApiKeyParams = SwaggerApiKeyParams { -- | The name of the header or query parameter to be used.- swaggerApiKeyName :: Text+ _swaggerApiKeyName :: Text -- | The location of the API key.- , swaggerApiKeyIn :: SwaggerApiKeyLocation+ , _swaggerApiKeyIn :: SwaggerApiKeyLocation } deriving (Eq, Show) -- | The authorization URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.@@ -572,10 +572,10 @@ data SwaggerOAuth2Params = SwaggerOAuth2Params { -- | The flow used by the OAuth2 security scheme.- swaggerOAuth2Flow :: SwaggerOAuth2Flow+ _swaggerOAuth2Flow :: SwaggerOAuth2Flow -- | The available scopes for the OAuth2 security scheme.- , swaggerOAuth2Scopes :: HashMap Text Text+ , _swaggerOAuth2Scopes :: HashMap Text Text } deriving (Eq, Show, Generic) data SwaggerSecuritySchemeType@@ -586,10 +586,10 @@ data SwaggerSecurityScheme = SwaggerSecurityScheme { -- | The type of the security scheme.- swaggerSecuritySchemeType :: SwaggerSecuritySchemeType+ _swaggerSecuritySchemeType :: SwaggerSecuritySchemeType -- | A short description for security scheme.- , swaggerSecuritySchemeDescription :: Maybe Text+ , _swaggerSecuritySchemeDescription :: Maybe Text } deriving (Eq, Show, Generic) -- | Lists the required security schemes to execute this operation.@@ -603,26 +603,36 @@ -- It is not mandatory to have a @SwaggerTag@ per tag used there. data SwaggerTag = SwaggerTag { -- | The name of the tag.- swaggerTagName :: Text+ _swaggerTagName :: Text -- | A short description for the tag. -- GFM syntax can be used for rich text representation.- , swaggerTagDescription :: Maybe Text+ , _swaggerTagDescription :: Maybe Text -- | Additional external documentation for this tag.- , swaggerTagExternalDocs :: Maybe SwaggerExternalDocs+ , _swaggerTagExternalDocs :: Maybe SwaggerExternalDocs } deriving (Eq, Show) -- | Allows referencing an external resource for extended documentation. data SwaggerExternalDocs = SwaggerExternalDocs { -- | A short description of the target documentation. -- GFM syntax can be used for rich text representation.- swaggerExternalDocsDescription :: Maybe Text+ _swaggerExternalDocsDescription :: Maybe Text -- | The URL for the target documentation.- , swaggerExternalDocsUrl :: URL+ , _swaggerExternalDocsUrl :: URL } deriving (Eq, Show, Generic) +-- | A simple object to allow referencing other definitions in the specification.+-- It can be used to reference parameters and responses that are defined at the top level for reuse.+newtype SwaggerReference = SwaggerReference { getSwaggerReference :: Text }+ deriving (Eq, Show)++data SwaggerReferenced a+ = SwaggerRef SwaggerReference+ | SwaggerInline a+ deriving (Eq, Show)+ newtype URL = URL { getUrl :: Text } deriving (Eq, Show, ToJSON, FromJSON) -- =======================================================================@@ -712,6 +722,15 @@ swaggerMempty = HashMap.empty swaggerMappend = HashMap.unionWith mappend +instance SwaggerMonoid (HashMap Text (SwaggerReferenced SwaggerSchema)) where+ swaggerMempty = HashMap.empty+ swaggerMappend = HashMap.unionWith swaggerMappend++instance Monoid a => SwaggerMonoid (SwaggerReferenced a) where+ swaggerMempty = SwaggerInline mempty+ swaggerMappend (SwaggerInline x) (SwaggerInline y) = SwaggerInline (x <> y)+ swaggerMappend _ y = y+ instance SwaggerMonoid (HashMap Text SwaggerParameter) where swaggerMempty = HashMap.empty swaggerMappend = HashMap.unionWith mappend@@ -732,7 +751,7 @@ swaggerMempty = HashMap.empty swaggerMappend = flip HashMap.union -instance SwaggerMonoid (HashMap HttpStatusCode SwaggerResponse) where+instance SwaggerMonoid (HashMap HttpStatusCode (SwaggerReferenced SwaggerResponse)) where swaggerMempty = HashMap.empty swaggerMappend = flip HashMap.union @@ -761,11 +780,11 @@ deriveJSONDefault ''SwaggerScheme deriveJSON' ''SwaggerTag deriveJSON' ''SwaggerExternalDocs-deriveJSON' ''SwaggerXml deriveToJSON' ''SwaggerOperation deriveToJSON' ''SwaggerResponse deriveToJSON' ''SwaggerPathItem+deriveToJSON' ''SwaggerXml -- ======================================================================= -- Manual ToJSON instances@@ -833,7 +852,7 @@ toJSON = genericToJSONWithSub "schema" (jsonPrefix "swaggerParameter") instance ToJSON SwaggerParameterSchema where- toJSON (SwaggerParameterBody s) = toJSON s <+> object [ "in" .= ("body" :: Text) ]+ toJSON (SwaggerParameterBody s) = object [ "in" .= ("body" :: Text), "schema" .= s ] toJSON (SwaggerParameterOther s) = toJSON s instance ToJSON SwaggerParameterOtherSchema where@@ -849,6 +868,13 @@ instance ToJSON SwaggerExample where toJSON = toJSON . Map.mapKeys show . getSwaggerExample +instance ToJSON SwaggerReference where+ toJSON (SwaggerReference ref) = object [ "$ref" .= ref ]++instance ToJSON a => ToJSON (SwaggerReferenced a) where+ toJSON (SwaggerRef ref) = toJSON ref+ toJSON (SwaggerInline x) = toJSON x+ -- ======================================================================= -- Manual FromJSON instances -- =======================================================================@@ -912,10 +938,10 @@ instance FromJSON SwaggerHost where parseJSON (String s) = case fromInteger <$> readMaybe portStr of- Nothing | not (null portStr) -> empty+ Nothing | not (null portStr) -> fail $ "Invalid port `" ++ portStr ++ "'" mport -> pure $ SwaggerHost host mport where- (hostText, portText) = Text.breakOnEnd ":" s+ (hostText, portText) = Text.breakOn ":" s [host, portStr] = map Text.unpack [hostText, portText] parseJSON _ = empty @@ -933,7 +959,9 @@ parseJSON json@(Object o) = do (i :: Text) <- o .: "in" case i of- "body" -> SwaggerParameterBody <$> parseJSON json+ "body" -> do+ schema <- o .: "schema"+ SwaggerParameterBody <$> parseJSON schema _ -> SwaggerParameterOther <$> parseJSON json parseJSON _ = empty @@ -969,4 +997,17 @@ instance FromJSON SwaggerPathItem where parseJSON = genericParseJSON (jsonPrefix "swaggerPathItem") `withDefaults` [ "parameters" .= ([] :: [SwaggerParameter]) ]++instance FromJSON SwaggerReference where+ parseJSON (Object o) = SwaggerReference <$> o .: "$ref"+ parseJSON _ = empty++instance FromJSON a => FromJSON (SwaggerReferenced a) where+ parseJSON json+ = SwaggerRef <$> parseJSON json+ <|> SwaggerInline <$> parseJSON json++instance FromJSON SwaggerXml where+ parseJSON = genericParseJSON (jsonPrefix "swaggerXml")+ `withDefaults` [ "attribute" .= False, "wrapped" .= False ]
src/Data/Swagger/Internal/Utils.hs view
@@ -32,7 +32,7 @@ jsonPrefix :: String -> Options jsonPrefix prefix = defaultOptions- { fieldLabelModifier = modifier+ { fieldLabelModifier = modifier . drop 1 , constructorTagModifier = modifier , sumEncoding = ObjectWithSingleField }
+ src/Data/Swagger/Lens.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+module Data.Swagger.Lens where++import Control.Lens+import Control.Lens.TH+import Data.Aeson (Value)+import Data.Swagger.Internal+import Data.Text (Text)++-- =======================================================================+-- TH derived lenses+-- =======================================================================++makeLenses ''Swagger+makeLenses ''SwaggerHost+makeLenses ''SwaggerInfo+makeLenses ''SwaggerContact+makeLenses ''SwaggerLicense+makeLenses ''SwaggerPaths+makeLenses ''SwaggerPathItem+makeLenses ''SwaggerTag+makeLenses ''SwaggerOperation+makeLenses ''SwaggerParameter+makePrisms ''SwaggerParameterSchema+makeLenses ''SwaggerParameterOtherSchema+makeLenses ''SwaggerItems+makeLenses ''SwaggerHeader+makeLenses ''SwaggerSchema+makePrisms ''SwaggerSchemaItems+makeLenses ''SwaggerSchemaCommon+makeLenses ''SwaggerXml+makeLenses ''SwaggerResponses+makeLenses ''SwaggerResponse+makeLenses ''SwaggerSecurityScheme+makePrisms ''SwaggerSecuritySchemeType+makeLenses ''SwaggerApiKeyParams+makeLenses ''SwaggerOAuth2Params+makeLenses ''SwaggerExternalDocs++-- =======================================================================+-- Helper classy lenses+-- =======================================================================++class HasDescription s d | s -> d where+ description :: Lens' s d++instance HasDescription SwaggerResponse Text where description = swaggerResponseDescription+instance HasDescription SwaggerInfo (Maybe Text) where description = swaggerInfoDescription+instance HasDescription SwaggerTag (Maybe Text) where description = swaggerTagDescription+instance HasDescription SwaggerOperation (Maybe Text) where description = swaggerOperationDescription+instance HasDescription SwaggerParameter (Maybe Text) where description = swaggerParameterDescription+instance HasDescription SwaggerHeader (Maybe Text) where description = swaggerHeaderDescription+instance HasDescription SwaggerSchema (Maybe Text) where description = swaggerSchemaDescription+instance HasDescription SwaggerSecurityScheme (Maybe Text) where description = swaggerSecuritySchemeDescription+instance HasDescription SwaggerExternalDocs (Maybe Text) where description = swaggerExternalDocsDescription++class HasSwaggerSchemaCommon s where+ schemaCommon :: Lens' s SwaggerSchemaCommon++instance HasSwaggerSchemaCommon SwaggerSchema where schemaCommon = swaggerSchemaCommon+instance HasSwaggerSchemaCommon SwaggerParameterOtherSchema where schemaCommon = swaggerParameterOtherSchemaCommon+instance HasSwaggerSchemaCommon SwaggerItems where schemaCommon = swaggerItemsCommon+instance HasSwaggerSchemaCommon SwaggerHeader where schemaCommon = swaggerHeaderCommon+instance HasSwaggerSchemaCommon SwaggerSchemaCommon where schemaCommon = id++schemaDefault :: HasSwaggerSchemaCommon s => Lens' s (Maybe Value)+schemaDefault = schemaCommon.swaggerSchemaDefault++schemaMaximum :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMaximum = schemaCommon.swaggerSchemaMaximum++schemaExclusiveMaximum :: HasSwaggerSchemaCommon s => Lens' s (Maybe Bool)+schemaExclusiveMaximum = schemaCommon.swaggerSchemaExclusiveMaximum++schemaMinimum :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMinimum = schemaCommon.swaggerSchemaMinimum++schemaExclusiveMinimum :: HasSwaggerSchemaCommon s => Lens' s (Maybe Bool)+schemaExclusiveMinimum = schemaCommon.swaggerSchemaExclusiveMinimum++schemaMaxLength :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMaxLength = schemaCommon.swaggerSchemaMaxLength++schemaMinLength :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMinLength = schemaCommon.swaggerSchemaMinLength++schemaPattern :: HasSwaggerSchemaCommon s => Lens' s (Maybe Text)+schemaPattern = schemaCommon.swaggerSchemaPattern++schemaMaxItems :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMaxItems = schemaCommon.swaggerSchemaMaxItems++schemaMinItems :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMinItems = schemaCommon.swaggerSchemaMinItems++schemaUniqueItems :: HasSwaggerSchemaCommon s => Lens' s (Maybe Bool)+schemaUniqueItems = schemaCommon.swaggerSchemaUniqueItems++schemaEnum :: HasSwaggerSchemaCommon s => Lens' s (Maybe [Value])+schemaEnum = schemaCommon.swaggerSchemaEnum++schemaMultipleOf :: HasSwaggerSchemaCommon s => Lens' s (Maybe Integer)+schemaMultipleOf = schemaCommon.swaggerSchemaMultipleOf+
swagger2.cabal view
@@ -1,5 +1,5 @@ name: swagger2-version: 0.1+version: 0.2 synopsis: Swagger 2.0 data model description: Please see README.md homepage: https://github.com/GetShopTV/swagger2@@ -12,12 +12,16 @@ build-type: Simple extra-source-files: README.md+ , CHANGELOG.md cabal-version: >=1.10 library hs-source-dirs: src exposed-modules: Data.Swagger+ Data.Swagger.Lens++ -- internal modules Data.Swagger.Internal Data.Swagger.Internal.Utils build-depends: base == 4.*@@ -29,6 +33,7 @@ , template-haskell , text , unordered-containers+ , lens default-language: Haskell2010 test-suite spec
test/Data/SwaggerSpec.hs view
@@ -1,612 +1,1659 @@ {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-module Data.SwaggerSpec where--import Data.Aeson-import Data.Aeson.QQ-import qualified Data.Foldable as F-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as HashMap-import Data.Maybe-import Data.Monoid-import qualified Data.Vector as Vector-import Data.Text (Text)--import Data.Swagger--import Test.Hspec--isSubJSON :: Value -> Value -> Bool-isSubJSON Null _ = True-isSubJSON (Object x) (Object y) = HashMap.keys x == HashMap.keys i && F.and i- where- i = HashMap.intersectionWith isSubJSON x y-isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)-isSubJSON x y = x == y--(<~>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec-x <~> json = do- it "encodes correctly (probably with extra properties)" $ do- toJSON x `shouldSatisfy` (json `isSubJSON`)- it "decodes correctly" $ do- fromJSON json `shouldBe` Success x--(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec-x <=> json = do- it "encodes correctly" $ do- toJSON x `shouldBe` json- it "decodes correctly" $ do- fromJSON json `shouldBe` Success x--spec :: Spec-spec = do- describe "License Object" $ licenseExample <=> licenseExampleJSON- describe "Contact Object" $ contactExample <=> contactExampleJSON- describe "Info Object" $ infoExample <=> infoExampleJSON- describe "Operation Object" $ operationExample <~> operationExampleJSON- describe "Schema Object" $ do- context "Primitive Sample" $ schemaPrimitiveExample <~> schemaPrimitiveExampleJSON- context "Simple Model" $ schemaSimpleModelExample <~> schemaSimpleModelExampleJSON- context "Model with Map/Dictionary Properties" $ schemaModelDictExample <~> schemaModelDictExampleJSON- context "Model with Example" $ schemaWithExampleExample <~> schemaWithExampleExampleJSON- describe "Definitions Object" $ definitionsExample <~> definitionsExampleJSON- describe "Parameters Definition Object" $ parametersDefinitionExample <~> parametersDefinitionExampleJSON- describe "Responses Definition Object" $ responsesDefinitionExample <~> responsesDefinitionExampleJSON- describe "Security Definitions Object" $ securityDefinitionsExample <~> securityDefinitionsExampleJSON- describe "Swagger Object" $ swaggerExample <~> swaggerExampleJSON--main :: IO ()-main = hspec spec---- =======================================================================--- Info object--- =======================================================================--infoExample :: SwaggerInfo-infoExample = SwaggerInfo- { swaggerInfoTitle = "Swagger Sample App"- , swaggerInfoDescription = Just "This is a sample server Petstore server."- , swaggerInfoTermsOfService = Just "http://swagger.io/terms/"- , swaggerInfoContact = Just contactExample- , swaggerInfoLicense = Just licenseExample- , swaggerInfoVersion = "1.0.1" }- where- license = SwaggerLicense- { swaggerLicenseName = "Apache 2.0"- , swaggerLicenseUrl = Just (URL "http://www.apache.org/licenses/LICENSE-2.0.html") }--infoExampleJSON :: Value-infoExampleJSON = [aesonQQ|-{- "title": "Swagger Sample App",- "description": "This is a sample server Petstore server.",- "termsOfService": "http://swagger.io/terms/",- "contact": {- "name": "API Support",- "url": "http://www.swagger.io/support",- "email": "support@swagger.io"- },- "license": {- "name": "Apache 2.0",- "url": "http://www.apache.org/licenses/LICENSE-2.0.html"- },- "version": "1.0.1"-}-|]---- =======================================================================--- Contact object--- =======================================================================--contactExample :: SwaggerContact-contactExample = SwaggerContact- { swaggerContactName = Just "API Support"- , swaggerContactUrl = Just (URL "http://www.swagger.io/support")- , swaggerContactEmail = Just "support@swagger.io" }--contactExampleJSON :: Value-contactExampleJSON = [aesonQQ|-{- "name": "API Support",- "url": "http://www.swagger.io/support",- "email": "support@swagger.io"-}-|]---- =======================================================================--- License object--- =======================================================================--licenseExample :: SwaggerLicense-licenseExample = SwaggerLicense- { swaggerLicenseName = "Apache 2.0"- , swaggerLicenseUrl = Just (URL "http://www.apache.org/licenses/LICENSE-2.0.html") }--licenseExampleJSON :: Value-licenseExampleJSON = [aesonQQ|-{- "name": "Apache 2.0",- "url": "http://www.apache.org/licenses/LICENSE-2.0.html"-}-|]----- =======================================================================--- Operation object--- =======================================================================--operationExample :: SwaggerOperation-operationExample = mempty- { swaggerOperationTags = ["pet"]- , swaggerOperationSummary = Just "Updates a pet in the store with form data"- , swaggerOperationDescription = Just ""- , swaggerOperationOperationId = Just "updatePetWithForm"- , swaggerOperationConsumes = Just (SwaggerMimeList ["application/x-www-form-urlencoded"])- , swaggerOperationProduces = Just (SwaggerMimeList ["application/json", "application/xml"])- , swaggerOperationParameters = params- , swaggerOperationResponses = responses- , swaggerOperationSecurity = security- }- where- security = [SwaggerSecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]-- responses = mempty- { swaggerResponsesResponses =- [ (200, mempty { swaggerResponseDescription = "Pet updated." })- , (405, mempty { swaggerResponseDescription = "Invalid input" }) ] }-- params =- [ SwaggerParameter- { swaggerParameterName = "petId"- , swaggerParameterDescription = Just "ID of pet that needs to be updated"- , swaggerParameterRequired = True- , swaggerParameterSchema = SwaggerParameterOther (stringSchema SwaggerParameterPath) }- , SwaggerParameter- { swaggerParameterName = "name"- , swaggerParameterDescription = Just "Updated name of the pet"- , swaggerParameterRequired = False- , swaggerParameterSchema = SwaggerParameterOther (stringSchema SwaggerParameterFormData) }- , SwaggerParameter- { swaggerParameterName = "status"- , swaggerParameterDescription = Just "Updated status of the pet"- , swaggerParameterRequired = False- , swaggerParameterSchema = SwaggerParameterOther (stringSchema SwaggerParameterFormData) }- ]-- stringSchema i = mempty- { swaggerParameterOtherSchemaIn = i- , swaggerParameterOtherSchemaType = SwaggerParamString- }--operationExampleJSON :: Value-operationExampleJSON = [aesonQQ|-{- "tags": [- "pet"- ],- "summary": "Updates a pet in the store with form data",- "description": "",- "operationId": "updatePetWithForm",- "consumes": [- "application/x-www-form-urlencoded"- ],- "produces": [- "application/json",- "application/xml"- ],- "parameters": [- {- "name": "petId",- "in": "path",- "description": "ID of pet that needs to be updated",- "required": true,- "type": "string"- },- {- "name": "name",- "in": "formData",- "description": "Updated name of the pet",- "required": false,- "type": "string"- },- {- "name": "status",- "in": "formData",- "description": "Updated status of the pet",- "required": false,- "type": "string"- }- ],- "responses": {- "200": {- "description": "Pet updated."- },- "405": {- "description": "Invalid input"- }- },- "security": [- {- "petstore_auth": [- "write:pets",- "read:pets"- ]- }- ]-}-|]---- =======================================================================--- Schema object--- =======================================================================--schemaPrimitiveExample :: SwaggerSchema-schemaPrimitiveExample = mempty- { swaggerSchemaType = SwaggerSchemaString- , swaggerSchemaFormat = Just "email"- }--schemaPrimitiveExampleJSON :: Value-schemaPrimitiveExampleJSON = [aesonQQ|-{- "type": "string",- "format": "email"-}-|]--schemaSimpleModelExample :: SwaggerSchema-schemaSimpleModelExample = mempty- { swaggerSchemaType = SwaggerSchemaObject- , swaggerSchemaRequired = [ "name" ]- , swaggerSchemaProperties =- [ ("name", mempty- { swaggerSchemaType = SwaggerSchemaString } )- , ("age", mempty- { swaggerSchemaType = SwaggerSchemaInteger- , swaggerSchemaFormat = Just "int32"- , swaggerSchemaCommon = mempty- { swaggerSchemaMinimum = Just 0 } } ) ] }--schemaSimpleModelExampleJSON :: Value-schemaSimpleModelExampleJSON = [aesonQQ|-{- "type": "object",- "required": [- "name"- ],- "properties": {- "name": {- "type": "string"- },- "age": {- "type": "integer",- "format": "int32",- "minimum": 0- }- }-}-|]--schemaModelDictExample :: SwaggerSchema-schemaModelDictExample = mempty- { swaggerSchemaType = SwaggerSchemaObject- , swaggerSchemaAdditionalProperties = Just mempty- { swaggerSchemaType = SwaggerSchemaString } }--schemaModelDictExampleJSON :: Value-schemaModelDictExampleJSON = [aesonQQ|-{- "type": "object",- "additionalProperties": {- "type": "string"- }-}-|]--schemaWithExampleExample :: SwaggerSchema-schemaWithExampleExample = mempty- { swaggerSchemaType = SwaggerSchemaObject- , swaggerSchemaProperties =- [ ("id", mempty- { swaggerSchemaType = SwaggerSchemaInteger- , swaggerSchemaFormat = Just "int64" })- , ("name", mempty- { swaggerSchemaType = SwaggerSchemaString }) ]- , swaggerSchemaRequired = [ "name" ]- , swaggerSchemaExample = Just [aesonQQ|- {- "name": "Puma",- "id": 1- }- |] }--schemaWithExampleExampleJSON :: Value-schemaWithExampleExampleJSON = [aesonQQ|-{- "type": "object",- "properties": {- "id": {- "type": "integer",- "format": "int64"- },- "name": {- "type": "string"- }- },- "required": [- "name"- ],- "example": {- "name": "Puma",- "id": 1- }-}-|]---- =======================================================================--- Definitions object--- =======================================================================--definitionsExample :: HashMap Text SwaggerSchema-definitionsExample =- [ ("Category", mempty- { swaggerSchemaType = SwaggerSchemaObject- , swaggerSchemaProperties =- [ ("id", mempty- { swaggerSchemaType = SwaggerSchemaInteger- , swaggerSchemaFormat = Just "int64" })- , ("name", mempty- { swaggerSchemaType = SwaggerSchemaString }) ] })- , ("Tag", mempty- { swaggerSchemaType = SwaggerSchemaObject- , swaggerSchemaProperties =- [ ("id", mempty- { swaggerSchemaType = SwaggerSchemaInteger- , swaggerSchemaFormat = Just "int64" })- , ("name", mempty- { swaggerSchemaType = SwaggerSchemaString }) ] }) ]--definitionsExampleJSON :: Value-definitionsExampleJSON = [aesonQQ|-{- "Category": {- "type": "object",- "properties": {- "id": {- "type": "integer",- "format": "int64"- },- "name": {- "type": "string"- }- }- },- "Tag": {- "type": "object",- "properties": {- "id": {- "type": "integer",- "format": "int64"- },- "name": {- "type": "string"- }- }- }-}-|]---- =======================================================================--- Parameters Definition object--- =======================================================================--parametersDefinitionExample :: HashMap Text SwaggerParameter-parametersDefinitionExample =- [ ("skipParam", mempty- { swaggerParameterName = "skip"- , swaggerParameterDescription = Just "number of items to skip"- , swaggerParameterRequired = True- , swaggerParameterSchema = SwaggerParameterOther mempty- { swaggerParameterOtherSchemaIn = SwaggerParameterQuery- , swaggerParameterOtherSchemaType = SwaggerParamInteger- , swaggerParameterOtherSchemaFormat = Just "int32" } })- , ("limitParam", mempty- { swaggerParameterName = "limit"- , swaggerParameterDescription = Just "max records to return"- , swaggerParameterRequired = True- , swaggerParameterSchema = SwaggerParameterOther mempty- { swaggerParameterOtherSchemaIn = SwaggerParameterQuery- , swaggerParameterOtherSchemaType = SwaggerParamInteger- , swaggerParameterOtherSchemaFormat = Just "int32" } }) ]--parametersDefinitionExampleJSON :: Value-parametersDefinitionExampleJSON = [aesonQQ|-{- "skipParam": {- "name": "skip",- "in": "query",- "description": "number of items to skip",- "required": true,- "type": "integer",- "format": "int32"- },- "limitParam": {- "name": "limit",- "in": "query",- "description": "max records to return",- "required": true,- "type": "integer",- "format": "int32"- }-}-|]---- =======================================================================--- Responses Definition object--- =======================================================================--responsesDefinitionExample :: HashMap Text SwaggerResponse-responsesDefinitionExample =- [ ("NotFound", mempty { swaggerResponseDescription = "Entity not found." })- , ("IllegalInput", mempty { swaggerResponseDescription = "Illegal input for operation." }) ]--responsesDefinitionExampleJSON :: Value-responsesDefinitionExampleJSON = [aesonQQ|-{- "NotFound": {- "description": "Entity not found."- },- "IllegalInput": {- "description": "Illegal input for operation."- }-}-|]---- =======================================================================--- Responses Definition object--- =======================================================================--securityDefinitionsExample :: HashMap Text SwaggerSecurityScheme-securityDefinitionsExample =- [ ("api_key", SwaggerSecurityScheme- { swaggerSecuritySchemeType = SwaggerSecuritySchemeApiKey (SwaggerApiKeyParams "api_key" SwaggerApiKeyHeader)- , swaggerSecuritySchemeDescription = Nothing })- , ("petstore_auth", SwaggerSecurityScheme- { swaggerSecuritySchemeType = SwaggerSecuritySchemeOAuth2 (SwaggerOAuth2Params- { swaggerOAuth2Flow = SwaggerOAuth2Implicit "http://swagger.io/api/oauth/dialog"- , swaggerOAuth2Scopes =- [ ("write:pets", "modify pets in your account")- , ("read:pets", "read your pets") ] } )- , swaggerSecuritySchemeDescription = Nothing }) ]--securityDefinitionsExampleJSON :: Value-securityDefinitionsExampleJSON = [aesonQQ|-{- "api_key": {- "type": "apiKey",- "name": "api_key",- "in": "header"- },- "petstore_auth": {- "type": "oauth2",- "authorizationUrl": "http://swagger.io/api/oauth/dialog",- "flow": "implicit",- "scopes": {- "write:pets": "modify pets in your account",- "read:pets": "read your pets"- }- }-}-|]---- =======================================================================--- Swagger object--- =======================================================================--swaggerExample :: Swagger-swaggerExample = mempty- { swaggerBasePath = Just "/"- , swaggerSchemes = Just [Http]- , swaggerInfo = mempty- { swaggerInfoVersion = "1.0"- , swaggerInfoTitle = "Todo API"- , swaggerInfoLicense = Just SwaggerLicense- { swaggerLicenseName = "MIT"- , swaggerLicenseUrl = Just (URL "http://mit.com") }- , swaggerInfoDescription = Just "This is a an API that tests servant-swagger support for a Todo API" }- , swaggerPaths = mempty- { swaggerPathsMap =- [ ("/todo/{id}", mempty- { swaggerPathItemGet = Just mempty- { swaggerOperationResponses = mempty- { swaggerResponsesResponses =- [ (200, mempty- { swaggerResponseSchema = Just mempty- { swaggerSchemaExample = Just [aesonQQ|- {- "created": 100,- "description": "get milk"- } |]- , swaggerSchemaType = SwaggerSchemaObject- , swaggerSchemaDescription = Just "This is some real Todo right here"- , swaggerSchemaProperties =- [ ("created", mempty- { swaggerSchemaType = SwaggerSchemaInteger- , swaggerSchemaFormat = Just "int32" })- , ("description", mempty- { swaggerSchemaType = SwaggerSchemaString }) ] }- , swaggerResponseDescription = "OK" }) ] }- , swaggerOperationProduces = Just (SwaggerMimeList [ "application/json" ])- , swaggerOperationParameters =- [ mempty- { swaggerParameterRequired = True- , swaggerParameterName = "id"- , swaggerParameterDescription = Just "TodoId param"- , swaggerParameterSchema = SwaggerParameterOther mempty- { swaggerParameterOtherSchemaIn = SwaggerParameterPath- , swaggerParameterOtherSchemaType = SwaggerParamString } } ]- , swaggerOperationTags = [ "todo" ] } }) ] } }--swaggerExampleJSON :: Value-swaggerExampleJSON = [aesonQQ|-{- "swagger": "2.0",- "basePath": "/",- "schemes": [- "http"- ],- "info": {- "version": "1.0",- "title": "Todo API",- "license": {- "url": "http://mit.com",- "name": "MIT"- },- "description": "This is a an API that tests servant-swagger support for a Todo API"- },- "paths": {- "/todo/{id}": {- "get": {- "responses": {- "200": {- "schema": {- "example": {- "created": 100,- "description": "get milk"- },- "type": "object",- "description": "This is some real Todo right here",- "properties": {- "created": {- "format": "int32",- "type": "integer"- },- "description": {- "type": "string"- }- }- },- "description": "OK"- }- },- "produces": [- "application/json"- ],- "parameters": [- {- "required": true,- "in": "path",- "name": "id",- "type": "string",- "description": "TodoId param"- }- ],- "tags": [- "todo"- ]- }- }- }+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE QuasiQuotes #-}+module Data.SwaggerSpec where++import Data.Aeson+import Data.Aeson.QQ+import qualified Data.Foldable as F+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap+import Data.Maybe+import Data.Monoid+import qualified Data.Vector as Vector+import Data.Text (Text)++import Data.Swagger++import Test.Hspec++isSubJSON :: Value -> Value -> Bool+isSubJSON Null _ = True+isSubJSON (Object x) (Object y) = HashMap.keys x == HashMap.keys i && F.and i+ where+ i = HashMap.intersectionWith isSubJSON x y+isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)+isSubJSON x y = x == y++(<~>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec+x <~> json = do+ it "encodes correctly (probably with extra properties)" $ do+ toJSON x `shouldSatisfy` (json `isSubJSON`)+ it "decodes correctly" $ do+ fromJSON json `shouldBe` Success x++(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec+x <=> json = do+ it "encodes correctly" $ do+ toJSON x `shouldBe` json+ it "decodes correctly" $ do+ fromJSON json `shouldBe` Success x++spec :: Spec+spec = do+ describe "License Object" $ licenseExample <=> licenseExampleJSON+ describe "Contact Object" $ contactExample <=> contactExampleJSON+ describe "Info Object" $ infoExample <=> infoExampleJSON+ describe "Operation Object" $ operationExample <~> operationExampleJSON+ describe "Schema Object" $ do+ context "Primitive Sample" $ schemaPrimitiveExample <~> schemaPrimitiveExampleJSON+ context "Simple Model" $ schemaSimpleModelExample <~> schemaSimpleModelExampleJSON+ context "Model with Map/Dictionary Properties" $ schemaModelDictExample <~> schemaModelDictExampleJSON+ context "Model with Example" $ schemaWithExampleExample <~> schemaWithExampleExampleJSON+ describe "Definitions Object" $ definitionsExample <~> definitionsExampleJSON+ describe "Parameters Definition Object" $ parametersDefinitionExample <~> parametersDefinitionExampleJSON+ describe "Responses Definition Object" $ responsesDefinitionExample <~> responsesDefinitionExampleJSON+ describe "Security Definitions Object" $ securityDefinitionsExample <~> securityDefinitionsExampleJSON+ describe "Swagger Object" $ do+ context "Todo Example" $ swaggerExample <~> swaggerExampleJSON+ context "PetStore Example" $+ it "decodes successfully" $ do+ fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: Swagger) -> True; _ -> False)++main :: IO ()+main = hspec spec++-- =======================================================================+-- Info object+-- =======================================================================++infoExample :: SwaggerInfo+infoExample = SwaggerInfo+ { _swaggerInfoTitle = "Swagger Sample App"+ , _swaggerInfoDescription = Just "This is a sample server Petstore server."+ , _swaggerInfoTermsOfService = Just "http://swagger.io/terms/"+ , _swaggerInfoContact = Just contactExample+ , _swaggerInfoLicense = Just licenseExample+ , _swaggerInfoVersion = "1.0.1" }+ where+ license = SwaggerLicense+ { _swaggerLicenseName = "Apache 2.0"+ , _swaggerLicenseUrl = Just (URL "http://www.apache.org/licenses/LICENSE-2.0.html") }++infoExampleJSON :: Value+infoExampleJSON = [aesonQQ|+{+ "title": "Swagger Sample App",+ "description": "This is a sample server Petstore server.",+ "termsOfService": "http://swagger.io/terms/",+ "contact": {+ "name": "API Support",+ "url": "http://www.swagger.io/support",+ "email": "support@swagger.io"+ },+ "license": {+ "name": "Apache 2.0",+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"+ },+ "version": "1.0.1"+}+|]++-- =======================================================================+-- Contact object+-- =======================================================================++contactExample :: SwaggerContact+contactExample = SwaggerContact+ { _swaggerContactName = Just "API Support"+ , _swaggerContactUrl = Just (URL "http://www.swagger.io/support")+ , _swaggerContactEmail = Just "support@swagger.io" }++contactExampleJSON :: Value+contactExampleJSON = [aesonQQ|+{+ "name": "API Support",+ "url": "http://www.swagger.io/support",+ "email": "support@swagger.io"+}+|]++-- =======================================================================+-- License object+-- =======================================================================++licenseExample :: SwaggerLicense+licenseExample = SwaggerLicense+ { _swaggerLicenseName = "Apache 2.0"+ , _swaggerLicenseUrl = Just (URL "http://www.apache.org/licenses/LICENSE-2.0.html") }++licenseExampleJSON :: Value+licenseExampleJSON = [aesonQQ|+{+ "name": "Apache 2.0",+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"+}+|]+++-- =======================================================================+-- Operation object+-- =======================================================================++operationExample :: SwaggerOperation+operationExample = mempty+ { _swaggerOperationTags = ["pet"]+ , _swaggerOperationSummary = Just "Updates a pet in the store with form data"+ , _swaggerOperationDescription = Just ""+ , _swaggerOperationOperationId = Just "updatePetWithForm"+ , _swaggerOperationConsumes = Just (SwaggerMimeList ["application/x-www-form-urlencoded"])+ , _swaggerOperationProduces = Just (SwaggerMimeList ["application/json", "application/xml"])+ , _swaggerOperationParameters = params+ , _swaggerOperationResponses = responses+ , _swaggerOperationSecurity = security+ }+ where+ security = [SwaggerSecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]++ responses = mempty+ { _swaggerResponsesResponses =+ [ (200, SwaggerInline mempty { _swaggerResponseDescription = "Pet updated." })+ , (405, SwaggerInline mempty { _swaggerResponseDescription = "Invalid input" }) ] }++ params = map SwaggerInline+ [ SwaggerParameter+ { _swaggerParameterName = "petId"+ , _swaggerParameterDescription = Just "ID of pet that needs to be updated"+ , _swaggerParameterRequired = True+ , _swaggerParameterSchema = SwaggerParameterOther (stringSchema SwaggerParameterPath) }+ , SwaggerParameter+ { _swaggerParameterName = "name"+ , _swaggerParameterDescription = Just "Updated name of the pet"+ , _swaggerParameterRequired = False+ , _swaggerParameterSchema = SwaggerParameterOther (stringSchema SwaggerParameterFormData) }+ , SwaggerParameter+ { _swaggerParameterName = "status"+ , _swaggerParameterDescription = Just "Updated status of the pet"+ , _swaggerParameterRequired = False+ , _swaggerParameterSchema = SwaggerParameterOther (stringSchema SwaggerParameterFormData) }+ ]++ stringSchema i = mempty+ { _swaggerParameterOtherSchemaIn = i+ , _swaggerParameterOtherSchemaType = SwaggerParamString+ }++operationExampleJSON :: Value+operationExampleJSON = [aesonQQ|+{+ "tags": [+ "pet"+ ],+ "summary": "Updates a pet in the store with form data",+ "description": "",+ "operationId": "updatePetWithForm",+ "consumes": [+ "application/x-www-form-urlencoded"+ ],+ "produces": [+ "application/json",+ "application/xml"+ ],+ "parameters": [+ {+ "name": "petId",+ "in": "path",+ "description": "ID of pet that needs to be updated",+ "required": true,+ "type": "string"+ },+ {+ "name": "name",+ "in": "formData",+ "description": "Updated name of the pet",+ "required": false,+ "type": "string"+ },+ {+ "name": "status",+ "in": "formData",+ "description": "Updated status of the pet",+ "required": false,+ "type": "string"+ }+ ],+ "responses": {+ "200": {+ "description": "Pet updated."+ },+ "405": {+ "description": "Invalid input"+ }+ },+ "security": [+ {+ "petstore_auth": [+ "write:pets",+ "read:pets"+ ]+ }+ ]+}+|]++-- =======================================================================+-- Schema object+-- =======================================================================++schemaPrimitiveExample :: SwaggerSchema+schemaPrimitiveExample = mempty+ { _swaggerSchemaType = SwaggerSchemaString+ , _swaggerSchemaFormat = Just "email"+ }++schemaPrimitiveExampleJSON :: Value+schemaPrimitiveExampleJSON = [aesonQQ|+{+ "type": "string",+ "format": "email"+}+|]++schemaSimpleModelExample :: SwaggerSchema+schemaSimpleModelExample = mempty+ { _swaggerSchemaType = SwaggerSchemaObject+ , _swaggerSchemaRequired = [ "name" ]+ , _swaggerSchemaProperties =+ [ ("name", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaString } )+ , ("address", SwaggerRef (SwaggerReference "#/definitions/Address"))+ , ("age", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaInteger+ , _swaggerSchemaFormat = Just "int32"+ , _swaggerSchemaCommon = mempty+ { _swaggerSchemaMinimum = Just 0 } } ) ] }++schemaSimpleModelExampleJSON :: Value+schemaSimpleModelExampleJSON = [aesonQQ|+{+ "type": "object",+ "required": [+ "name"+ ],+ "properties": {+ "name": {+ "type": "string"+ },+ "address": {+ "$ref": "#/definitions/Address"+ },+ "age": {+ "type": "integer",+ "format": "int32",+ "minimum": 0+ }+ }+}+|]++schemaModelDictExample :: SwaggerSchema+schemaModelDictExample = mempty+ { _swaggerSchemaType = SwaggerSchemaObject+ , _swaggerSchemaAdditionalProperties = Just mempty+ { _swaggerSchemaType = SwaggerSchemaString } }++schemaModelDictExampleJSON :: Value+schemaModelDictExampleJSON = [aesonQQ|+{+ "type": "object",+ "additionalProperties": {+ "type": "string"+ }+}+|]++schemaWithExampleExample :: SwaggerSchema+schemaWithExampleExample = mempty+ { _swaggerSchemaType = SwaggerSchemaObject+ , _swaggerSchemaProperties =+ [ ("id", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaInteger+ , _swaggerSchemaFormat = Just "int64" })+ , ("name", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaString }) ]+ , _swaggerSchemaRequired = [ "name" ]+ , _swaggerSchemaExample = Just [aesonQQ|+ {+ "name": "Puma",+ "id": 1+ }+ |] }++schemaWithExampleExampleJSON :: Value+schemaWithExampleExampleJSON = [aesonQQ|+{+ "type": "object",+ "properties": {+ "id": {+ "type": "integer",+ "format": "int64"+ },+ "name": {+ "type": "string"+ }+ },+ "required": [+ "name"+ ],+ "example": {+ "name": "Puma",+ "id": 1+ }+}+|]++-- =======================================================================+-- Definitions object+-- =======================================================================++definitionsExample :: HashMap Text SwaggerSchema+definitionsExample =+ [ ("Category", mempty+ { _swaggerSchemaType = SwaggerSchemaObject+ , _swaggerSchemaProperties =+ [ ("id", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaInteger+ , _swaggerSchemaFormat = Just "int64" })+ , ("name", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaString }) ] })+ , ("Tag", mempty+ { _swaggerSchemaType = SwaggerSchemaObject+ , _swaggerSchemaProperties =+ [ ("id", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaInteger+ , _swaggerSchemaFormat = Just "int64" })+ , ("name", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaString }) ] }) ]++definitionsExampleJSON :: Value+definitionsExampleJSON = [aesonQQ|+{+ "Category": {+ "type": "object",+ "properties": {+ "id": {+ "type": "integer",+ "format": "int64"+ },+ "name": {+ "type": "string"+ }+ }+ },+ "Tag": {+ "type": "object",+ "properties": {+ "id": {+ "type": "integer",+ "format": "int64"+ },+ "name": {+ "type": "string"+ }+ }+ }+}+|]++-- =======================================================================+-- Parameters Definition object+-- =======================================================================++parametersDefinitionExample :: HashMap Text SwaggerParameter+parametersDefinitionExample =+ [ ("skipParam", mempty+ { _swaggerParameterName = "skip"+ , _swaggerParameterDescription = Just "number of items to skip"+ , _swaggerParameterRequired = True+ , _swaggerParameterSchema = SwaggerParameterOther mempty+ { _swaggerParameterOtherSchemaIn = SwaggerParameterQuery+ , _swaggerParameterOtherSchemaType = SwaggerParamInteger+ , _swaggerParameterOtherSchemaFormat = Just "int32" } })+ , ("limitParam", mempty+ { _swaggerParameterName = "limit"+ , _swaggerParameterDescription = Just "max records to return"+ , _swaggerParameterRequired = True+ , _swaggerParameterSchema = SwaggerParameterOther mempty+ { _swaggerParameterOtherSchemaIn = SwaggerParameterQuery+ , _swaggerParameterOtherSchemaType = SwaggerParamInteger+ , _swaggerParameterOtherSchemaFormat = Just "int32" } }) ]++parametersDefinitionExampleJSON :: Value+parametersDefinitionExampleJSON = [aesonQQ|+{+ "skipParam": {+ "name": "skip",+ "in": "query",+ "description": "number of items to skip",+ "required": true,+ "type": "integer",+ "format": "int32"+ },+ "limitParam": {+ "name": "limit",+ "in": "query",+ "description": "max records to return",+ "required": true,+ "type": "integer",+ "format": "int32"+ }+}+|]++-- =======================================================================+-- Responses Definition object+-- =======================================================================++responsesDefinitionExample :: HashMap Text SwaggerResponse+responsesDefinitionExample =+ [ ("NotFound", mempty { _swaggerResponseDescription = "Entity not found." })+ , ("IllegalInput", mempty { _swaggerResponseDescription = "Illegal input for operation." }) ]++responsesDefinitionExampleJSON :: Value+responsesDefinitionExampleJSON = [aesonQQ|+{+ "NotFound": {+ "description": "Entity not found."+ },+ "IllegalInput": {+ "description": "Illegal input for operation."+ }+}+|]++-- =======================================================================+-- Responses Definition object+-- =======================================================================++securityDefinitionsExample :: HashMap Text SwaggerSecurityScheme+securityDefinitionsExample =+ [ ("api_key", SwaggerSecurityScheme+ { _swaggerSecuritySchemeType = SwaggerSecuritySchemeApiKey (SwaggerApiKeyParams "api_key" SwaggerApiKeyHeader)+ , _swaggerSecuritySchemeDescription = Nothing })+ , ("petstore_auth", SwaggerSecurityScheme+ { _swaggerSecuritySchemeType = SwaggerSecuritySchemeOAuth2 (SwaggerOAuth2Params+ { _swaggerOAuth2Flow = SwaggerOAuth2Implicit "http://swagger.io/api/oauth/dialog"+ , _swaggerOAuth2Scopes =+ [ ("write:pets", "modify pets in your account")+ , ("read:pets", "read your pets") ] } )+ , _swaggerSecuritySchemeDescription = Nothing }) ]++securityDefinitionsExampleJSON :: Value+securityDefinitionsExampleJSON = [aesonQQ|+{+ "api_key": {+ "type": "apiKey",+ "name": "api_key",+ "in": "header"+ },+ "petstore_auth": {+ "type": "oauth2",+ "authorizationUrl": "http://swagger.io/api/oauth/dialog",+ "flow": "implicit",+ "scopes": {+ "write:pets": "modify pets in your account",+ "read:pets": "read your pets"+ }+ }+}+|]++-- =======================================================================+-- Swagger object+-- =======================================================================++swaggerExample :: Swagger+swaggerExample = mempty+ { _swaggerBasePath = Just "/"+ , _swaggerSchemes = Just [Http]+ , _swaggerInfo = mempty+ { _swaggerInfoVersion = "1.0"+ , _swaggerInfoTitle = "Todo API"+ , _swaggerInfoLicense = Just SwaggerLicense+ { _swaggerLicenseName = "MIT"+ , _swaggerLicenseUrl = Just (URL "http://mit.com") }+ , _swaggerInfoDescription = Just "This is a an API that tests servant-swagger support for a Todo API" }+ , _swaggerPaths = mempty+ { _swaggerPathsMap =+ [ ("/todo/{id}", mempty+ { _swaggerPathItemGet = Just mempty+ { _swaggerOperationResponses = mempty+ { _swaggerResponsesResponses =+ [ (200, SwaggerInline mempty+ { _swaggerResponseSchema = Just $ SwaggerInline mempty+ { _swaggerSchemaExample = Just [aesonQQ|+ {+ "created": 100,+ "description": "get milk"+ } |]+ , _swaggerSchemaType = SwaggerSchemaObject+ , _swaggerSchemaDescription = Just "This is some real Todo right here"+ , _swaggerSchemaProperties =+ [ ("created", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaInteger+ , _swaggerSchemaFormat = Just "int32" })+ , ("description", SwaggerInline mempty+ { _swaggerSchemaType = SwaggerSchemaString }) ] }+ , _swaggerResponseDescription = "OK" }) ] }+ , _swaggerOperationProduces = Just (SwaggerMimeList [ "application/json" ])+ , _swaggerOperationParameters =+ [ SwaggerInline mempty+ { _swaggerParameterRequired = True+ , _swaggerParameterName = "id"+ , _swaggerParameterDescription = Just "TodoId param"+ , _swaggerParameterSchema = SwaggerParameterOther mempty+ { _swaggerParameterOtherSchemaIn = SwaggerParameterPath+ , _swaggerParameterOtherSchemaType = SwaggerParamString } } ]+ , _swaggerOperationTags = [ "todo" ] } }) ] } }++swaggerExampleJSON :: Value+swaggerExampleJSON = [aesonQQ|+{+ "swagger": "2.0",+ "basePath": "/",+ "schemes": [+ "http"+ ],+ "info": {+ "version": "1.0",+ "title": "Todo API",+ "license": {+ "url": "http://mit.com",+ "name": "MIT"+ },+ "description": "This is a an API that tests servant-swagger support for a Todo API"+ },+ "paths": {+ "/todo/{id}": {+ "get": {+ "responses": {+ "200": {+ "schema": {+ "example": {+ "created": 100,+ "description": "get milk"+ },+ "type": "object",+ "description": "This is some real Todo right here",+ "properties": {+ "created": {+ "format": "int32",+ "type": "integer"+ },+ "description": {+ "type": "string"+ }+ }+ },+ "description": "OK"+ }+ },+ "produces": [+ "application/json"+ ],+ "parameters": [+ {+ "required": true,+ "in": "path",+ "name": "id",+ "type": "string",+ "description": "TodoId param"+ }+ ],+ "tags": [+ "todo"+ ]+ }+ }+ }+}+|]++petstoreExampleJSON :: Value+petstoreExampleJSON = [aesonQQ|+{ + "swagger":"2.0",+ "info":{ + "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.",+ "version":"1.0.0",+ "title":"Swagger Petstore",+ "termsOfService":"http://swagger.io/terms/",+ "contact":{ + "email":"apiteam@swagger.io"+ },+ "license":{ + "name":"Apache 2.0",+ "url":"http://www.apache.org/licenses/LICENSE-2.0.html"+ }+ },+ "host":"petstore.swagger.io",+ "basePath":"/v2",+ "tags":[ + { + "name":"pet",+ "description":"Everything about your Pets",+ "externalDocs":{ + "description":"Find out more",+ "url":"http://swagger.io"+ }+ },+ { + "name":"store",+ "description":"Access to Petstore orders"+ },+ { + "name":"user",+ "description":"Operations about user",+ "externalDocs":{ + "description":"Find out more about our store",+ "url":"http://swagger.io"+ }+ }+ ],+ "schemes":[ + "http"+ ],+ "paths":{ + "/pet":{ + "post":{ + "tags":[ + "pet"+ ],+ "summary":"Add a new pet to the store",+ "description":"",+ "operationId":"addPet",+ "consumes":[ + "application/json",+ "application/xml"+ ],+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "in":"body",+ "name":"body",+ "description":"Pet object that needs to be added to the store",+ "required":true,+ "schema":{ + "$ref":"#/definitions/Pet"+ }+ }+ ],+ "responses":{ + "405":{ + "description":"Invalid input"+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ },+ "put":{ + "tags":[ + "pet"+ ],+ "summary":"Update an existing pet",+ "description":"",+ "operationId":"updatePet",+ "consumes":[ + "application/json",+ "application/xml"+ ],+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "in":"body",+ "name":"body",+ "description":"Pet object that needs to be added to the store",+ "required":true,+ "schema":{ + "$ref":"#/definitions/Pet"+ }+ }+ ],+ "responses":{ + "400":{ + "description":"Invalid ID supplied"+ },+ "404":{ + "description":"Pet not found"+ },+ "405":{ + "description":"Validation exception"+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ }+ },+ "/pet/findByStatus":{ + "get":{ + "tags":[ + "pet"+ ],+ "summary":"Finds Pets by status",+ "description":"Multiple status values can be provided with comma seperated strings",+ "operationId":"findPetsByStatus",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"status",+ "in":"query",+ "description":"Status values that need to be considered for filter",+ "required":true,+ "type":"array",+ "items":{ + "type":"string",+ "enum":[ + "available",+ "pending",+ "sold"+ ],+ "default":"available"+ },+ "collectionFormat":"csv"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "type":"array",+ "items":{ + "$ref":"#/definitions/Pet"+ }+ }+ },+ "400":{ + "description":"Invalid status value"+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ }+ },+ "/pet/findByTags":{ + "get":{ + "tags":[ + "pet"+ ],+ "summary":"Finds Pets by tags",+ "description":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.",+ "operationId":"findPetsByTags",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"tags",+ "in":"query",+ "description":"Tags to filter by",+ "required":true,+ "type":"array",+ "items":{ + "type":"string"+ },+ "collectionFormat":"csv"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "type":"array",+ "items":{ + "$ref":"#/definitions/Pet"+ }+ }+ },+ "400":{ + "description":"Invalid tag value"+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ }+ },+ "/pet/{petId}":{ + "get":{ + "tags":[ + "pet"+ ],+ "summary":"Find pet by ID",+ "description":"Returns a single pet",+ "operationId":"getPetById",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"petId",+ "in":"path",+ "description":"ID of pet to return",+ "required":true,+ "type":"integer",+ "format":"int64"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "$ref":"#/definitions/Pet"+ }+ },+ "400":{ + "description":"Invalid ID supplied"+ },+ "404":{ + "description":"Pet not found"+ }+ },+ "security":[ + { + "api_key":[ + ]+ }+ ]+ },+ "post":{ + "tags":[ + "pet"+ ],+ "summary":"Updates a pet in the store with form data",+ "description":"",+ "operationId":"updatePetWithForm",+ "consumes":[ + "application/x-www-form-urlencoded"+ ],+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"petId",+ "in":"path",+ "description":"ID of pet that needs to be updated",+ "required":true,+ "type":"integer",+ "format":"int64"+ },+ { + "name":"name",+ "in":"formData",+ "description":"Updated name of the pet",+ "required":false,+ "type":"string"+ },+ { + "name":"status",+ "in":"formData",+ "description":"Updated status of the pet",+ "required":false,+ "type":"string"+ }+ ],+ "responses":{ + "405":{ + "description":"Invalid input"+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ },+ "delete":{ + "tags":[ + "pet"+ ],+ "summary":"Deletes a pet",+ "description":"",+ "operationId":"deletePet",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"api_key",+ "in":"header",+ "required":false,+ "type":"string"+ },+ { + "name":"petId",+ "in":"path",+ "description":"Pet id to delete",+ "required":true,+ "type":"integer",+ "format":"int64"+ }+ ],+ "responses":{ + "400":{ + "description":"Invalid pet value"+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ }+ },+ "/pet/{petId}/uploadImage":{ + "post":{ + "tags":[ + "pet"+ ],+ "summary":"uploads an image",+ "description":"",+ "operationId":"uploadFile",+ "consumes":[ + "multipart/form-data"+ ],+ "produces":[ + "application/json"+ ],+ "parameters":[ + { + "name":"petId",+ "in":"path",+ "description":"ID of pet to update",+ "required":true,+ "type":"integer",+ "format":"int64"+ },+ { + "name":"additionalMetadata",+ "in":"formData",+ "description":"Additional data to pass to server",+ "required":false,+ "type":"string"+ },+ { + "name":"file",+ "in":"formData",+ "description":"file to upload",+ "required":false,+ "type":"file"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "$ref":"#/definitions/ApiResponse"+ }+ }+ },+ "security":[ + { + "petstore_auth":[ + "write:pets",+ "read:pets"+ ]+ }+ ]+ }+ },+ "/store/inventory":{ + "get":{ + "tags":[ + "store"+ ],+ "summary":"Returns pet inventories by status",+ "description":"Returns a map of status codes to quantities",+ "operationId":"getInventory",+ "produces":[ + "application/json"+ ],+ "parameters":[ + ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "type":"object",+ "additionalProperties":{ + "type":"integer",+ "format":"int32"+ }+ }+ }+ },+ "security":[ + { + "api_key":[ + ]+ }+ ]+ }+ },+ "/store/order":{ + "post":{ + "tags":[ + "store"+ ],+ "summary":"Place an order for a pet",+ "description":"",+ "operationId":"placeOrder",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "in":"body",+ "name":"body",+ "description":"order placed for purchasing the pet",+ "required":true,+ "schema":{ + "$ref":"#/definitions/Order"+ }+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "$ref":"#/definitions/Order"+ }+ },+ "400":{ + "description":"Invalid Order"+ }+ }+ }+ },+ "/store/order/{orderId}":{ + "get":{ + "tags":[ + "store"+ ],+ "summary":"Find purchase order by ID",+ "description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",+ "operationId":"getOrderById",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"orderId",+ "in":"path",+ "description":"ID of pet that needs to be fetched",+ "required":true,+ "type":"integer",+ "maximum":5.0,+ "minimum":1.0,+ "format":"int64"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "$ref":"#/definitions/Order"+ }+ },+ "400":{ + "description":"Invalid ID supplied"+ },+ "404":{ + "description":"Order not found"+ }+ }+ },+ "delete":{ + "tags":[ + "store"+ ],+ "summary":"Delete purchase order by ID",+ "description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",+ "operationId":"deleteOrder",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"orderId",+ "in":"path",+ "description":"ID of the order that needs to be deleted",+ "required":true,+ "type":"string",+ "minimum":1.0+ }+ ],+ "responses":{ + "400":{ + "description":"Invalid ID supplied"+ },+ "404":{ + "description":"Order not found"+ }+ }+ }+ },+ "/user":{ + "post":{ + "tags":[ + "user"+ ],+ "summary":"Create user",+ "description":"This can only be done by the logged in user.",+ "operationId":"createUser",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "in":"body",+ "name":"body",+ "description":"Created user object",+ "required":true,+ "schema":{ + "$ref":"#/definitions/User"+ }+ }+ ],+ "responses":{ + "default":{ + "description":"successful operation"+ }+ }+ }+ },+ "/user/createWithArray":{ + "post":{ + "tags":[ + "user"+ ],+ "summary":"Creates list of users with given input array",+ "description":"",+ "operationId":"createUsersWithArrayInput",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "in":"body",+ "name":"body",+ "description":"List of user object",+ "required":true,+ "schema":{ + "type":"array",+ "items":{ + "$ref":"#/definitions/User"+ }+ }+ }+ ],+ "responses":{ + "default":{ + "description":"successful operation"+ }+ }+ }+ },+ "/user/createWithList":{ + "post":{ + "tags":[ + "user"+ ],+ "summary":"Creates list of users with given input array",+ "description":"",+ "operationId":"createUsersWithListInput",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "in":"body",+ "name":"body",+ "description":"List of user object",+ "required":true,+ "schema":{ + "type":"array",+ "items":{ + "$ref":"#/definitions/User"+ }+ }+ }+ ],+ "responses":{ + "default":{ + "description":"successful operation"+ }+ }+ }+ },+ "/user/login":{ + "get":{ + "tags":[ + "user"+ ],+ "summary":"Logs user into the system",+ "description":"",+ "operationId":"loginUser",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"username",+ "in":"query",+ "description":"The user name for login",+ "required":true,+ "type":"string"+ },+ { + "name":"password",+ "in":"query",+ "description":"The password for login in clear text",+ "required":true,+ "type":"string"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "type":"string"+ },+ "headers":{ + "X-Rate-Limit":{ + "type":"integer",+ "format":"int32",+ "description":"calls per hour allowed by the user"+ },+ "X-Expires-After":{ + "type":"string",+ "format":"date-time",+ "description":"date in UTC when toekn expires"+ }+ }+ },+ "400":{ + "description":"Invalid username/password supplied"+ }+ }+ }+ },+ "/user/logout":{ + "get":{ + "tags":[ + "user"+ ],+ "summary":"Logs out current logged in user session",+ "description":"",+ "operationId":"logoutUser",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + ],+ "responses":{ + "default":{ + "description":"successful operation"+ }+ }+ }+ },+ "/user/{username}":{ + "get":{ + "tags":[ + "user"+ ],+ "summary":"Get user by user name",+ "description":"",+ "operationId":"getUserByName",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"username",+ "in":"path",+ "description":"The name that needs to be fetched. Use user1 for testing. ",+ "required":true,+ "type":"string"+ }+ ],+ "responses":{ + "200":{ + "description":"successful operation",+ "schema":{ + "$ref":"#/definitions/User"+ }+ },+ "400":{ + "description":"Invalid username supplied"+ },+ "404":{ + "description":"User not found"+ }+ }+ },+ "put":{ + "tags":[ + "user"+ ],+ "summary":"Updated user",+ "description":"This can only be done by the logged in user.",+ "operationId":"updateUser",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"username",+ "in":"path",+ "description":"name that need to be deleted",+ "required":true,+ "type":"string"+ },+ { + "in":"body",+ "name":"body",+ "description":"Updated user object",+ "required":true,+ "schema":{ + "$ref":"#/definitions/User"+ }+ }+ ],+ "responses":{ + "400":{ + "description":"Invalid user supplied"+ },+ "404":{ + "description":"User not found"+ }+ }+ },+ "delete":{ + "tags":[ + "user"+ ],+ "summary":"Delete user",+ "description":"This can only be done by the logged in user.",+ "operationId":"deleteUser",+ "produces":[ + "application/xml",+ "application/json"+ ],+ "parameters":[ + { + "name":"username",+ "in":"path",+ "description":"The name that needs to be deleted",+ "required":true,+ "type":"string"+ }+ ],+ "responses":{ + "400":{ + "description":"Invalid username supplied"+ },+ "404":{ + "description":"User not found"+ }+ }+ }+ }+ },+ "securityDefinitions":{ + "petstore_auth":{ + "type":"oauth2",+ "authorizationUrl":"http://petstore.swagger.io/api/oauth/dialog",+ "flow":"implicit",+ "scopes":{ + "write:pets":"modify pets in your account",+ "read:pets":"read your pets"+ }+ },+ "api_key":{ + "type":"apiKey",+ "name":"api_key",+ "in":"header"+ }+ },+ "definitions":{ + "Order":{ + "type":"object",+ "properties":{ + "id":{ + "type":"integer",+ "format":"int64"+ },+ "petId":{ + "type":"integer",+ "format":"int64"+ },+ "quantity":{ + "type":"integer",+ "format":"int32"+ },+ "shipDate":{ + "type":"string",+ "format":"date-time"+ },+ "status":{ + "type":"string",+ "description":"Order Status",+ "enum":[ + "placed",+ "approved",+ "delivered"+ ]+ },+ "complete":{ + "type":"boolean",+ "default":false+ }+ },+ "xml":{ + "name":"Order"+ }+ },+ "Category":{ + "type":"object",+ "properties":{ + "id":{ + "type":"integer",+ "format":"int64"+ },+ "name":{ + "type":"string"+ }+ },+ "xml":{ + "name":"Category"+ }+ },+ "User":{ + "type":"object",+ "properties":{ + "id":{ + "type":"integer",+ "format":"int64"+ },+ "username":{ + "type":"string"+ },+ "firstName":{ + "type":"string"+ },+ "lastName":{ + "type":"string"+ },+ "email":{ + "type":"string"+ },+ "password":{ + "type":"string"+ },+ "phone":{ + "type":"string"+ },+ "userStatus":{ + "type":"integer",+ "format":"int32",+ "description":"User Status"+ }+ },+ "xml":{ + "name":"User"+ }+ },+ "Tag":{ + "type":"object",+ "properties":{ + "id":{ + "type":"integer",+ "format":"int64"+ },+ "name":{ + "type":"string"+ }+ },+ "xml":{ + "name":"Tag"+ }+ },+ "Pet":{ + "type":"object",+ "required":[ + "name",+ "photoUrls"+ ],+ "properties":{ + "id":{ + "type":"integer",+ "format":"int64"+ },+ "category":{ + "$ref":"#/definitions/Category"+ },+ "name":{ + "type":"string",+ "example":"doggie"+ },+ "photoUrls":{ + "type":"array",+ "xml":{ + "name":"photoUrl",+ "wrapped":true+ },+ "items":{ + "type":"string"+ }+ },+ "tags":{ + "type":"array",+ "xml":{ + "name":"tag",+ "wrapped":true+ },+ "items":{ + "$ref":"#/definitions/Tag"+ }+ },+ "status":{ + "type":"string",+ "description":"pet status in the store",+ "enum":[ + "available",+ "pending",+ "sold"+ ]+ }+ },+ "xml":{ + "name":"Pet"+ }+ },+ "ApiResponse":{ + "type":"object",+ "properties":{ + "code":{ + "type":"integer",+ "format":"int32"+ },+ "type":{ + "type":"string"+ },+ "message":{ + "type":"string"+ }+ }+ }+ },+ "externalDocs":{ + "description":"Find out more about Swagger",+ "url":"http://swagger.io"+ } } |]