openapi3-3.2.3: src/Data/OpenApi.hs
-- |
-- Module: Data.OpenApi
-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
-- Stability: experimental
--
-- Swagger™ is a project used to describe and document RESTful APIs.
--
-- The Swagger specification defines a set of files required to describe such an API.
-- These files can then be used by the Swagger-UI project to display the API
-- and Swagger-Codegen to generate clients in various languages.
-- Additional utilities can also take advantage of the resulting files, such as testing tools.
module Data.OpenApi (
-- * How to use this library
-- $howto
-- ** @'Monoid'@ instances
-- $monoids
-- ** Lenses and prisms
-- $lens
-- ** Schema specification
-- $schema
-- ** Manipulation
-- $manipulation
-- ** Validation
-- $validation
-- * Re-exports
module Data.OpenApi.Lens,
module Data.OpenApi.Optics,
module Data.OpenApi.Operation,
module Data.OpenApi.ParamSchema,
module Data.OpenApi.Schema,
module Data.OpenApi.Schema.Validation,
-- * Swagger specification
OpenApi(..),
Server(..),
ServerVariable(..),
Components(..),
-- ** Info types
Info(..),
Contact(..),
License(..),
-- ** PathItem
PathItem(..),
-- ** Operations
Operation(..),
Tag(..),
TagName,
-- ** Types and formats
OpenApiType(..),
Format,
Definitions,
Style(..),
-- ** Parameters
Param(..),
ParamLocation(..),
ParamName,
Header(..),
HeaderName,
Example(..),
RequestBody(..),
MediaTypeObject(..),
Encoding(..),
-- ** Schemas
Schema(..),
NamedSchema(..),
OpenApiItems(..),
Xml(..),
Pattern,
AdditionalProperties(..),
Discriminator(..),
-- ** Responses
Responses(..),
Response(..),
HttpStatusCode,
Link(..),
Callback(..),
-- ** Security
SecurityScheme(..),
SecuritySchemeType(..),
HttpSchemeType(..),
SecurityDefinitions(..),
SecurityRequirement(..),
-- *** API key
ApiKeyParams(..),
ApiKeyLocation(..),
-- *** OAuth2
OAuth2Flows(..),
OAuth2Flow(..),
OAuth2ImplicitFlow(..),
OAuth2PasswordFlow(..),
OAuth2ClientCredentialsFlow(..),
OAuth2AuthorizationCodeFlow(..),
AuthorizationURL,
TokenURL,
-- ** External documentation
ExternalDocs(..),
-- ** References
Reference(..),
Referenced(..),
-- ** Miscellaneous
MimeList(..),
URL(..),
) where
import Data.OpenApi.Lens
import Data.OpenApi.Optics ()
import Data.OpenApi.Operation
import Data.OpenApi.ParamSchema
import Data.OpenApi.Schema
import Data.OpenApi.Schema.Validation
import Data.OpenApi.Internal
-- $setup
-- >>> import Control.Lens
-- >>> import Data.Aeson
-- >>> import Data.Monoid
-- >>> import Data.Proxy
-- >>> import GHC.Generics
-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
-- >>> import Data.OpenApi.Internal
-- >>> import Data.OpenApi.Internal.Schema
-- >>> import qualified Data.HashMap.Strict.InsOrd as IOHM
-- >>> import Data.OpenApi.Internal.Utils
-- >>> import Data.OpenApi.Lens
-- >>> :set -XDeriveGeneric
-- >>> :set -XOverloadedStrings
-- >>> :set -fno-warn-missing-methods
-- $howto
--
-- This section explains how to use this library to work with Swagger specification.
-- $monoids
--
-- Virtually all types representing Swagger specification have @'Monoid'@ instances.
-- The @'Monoid'@ type class provides two methods — @'mempty'@ and @'mappend'@.
--
-- In this library you can use @'mempty'@ for a default/empty value. For instance:
--
-- >>> BSL.putStrLn $ encodePretty (mempty :: OpenApi)
-- {
-- "components": {},
-- "info": {
-- "title": "",
-- "version": ""
-- },
-- "openapi": "3.0.0",
-- "paths": {}
-- }
--
-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
-- That is because these properties are actually required ones.
--
-- You /should/ always override the default (empty) value for these properties,
-- although it is not strictly necessary:
--
-- >>> BSL.putStrLn $ encodePretty mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
-- {
-- "title": "Todo API",
-- "version": "1.0"
-- }
--
-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
--
-- >>> BSL.putStrLn $ encodePretty $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
-- {
-- "title": "Todo API",
-- "version": "1.0"
-- }
--
-- This can be useful for combining specifications of endpoints into a whole API specification:
--
-- @
-- \-\- /account subAPI specification
-- accountAPI :: OpenApi
--
-- \-\- /task subAPI specification
-- taskAPI :: OpenApi
--
-- \-\- while API specification is just a combination
-- \-\- of subAPIs' specifications
-- api :: OpenApi
-- api = accountAPI <> taskAPI
-- @
-- $lens
--
-- Note: if you're working with the <https://hackage.haskell.org/package/optics optics> library, take a look at "Data.OpenApi.Optics".
--
-- Since @'Swagger'@ has a fairly complex structure, lenses and prisms are used
-- to work comfortably with it. In combination with @'Monoid'@ instances, lenses
-- make it fairly simple to construct/modify any part of the specification:
--
-- >>> :{
-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
-- & components . schemas .~ IOHM.fromList [ ("User", mempty & type_ ?~ OpenApiString) ]
-- & paths .~
-- IOHM.fromList [ ("/user", mempty & get ?~ (mempty
-- & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User")))
-- & at 404 ?~ "User info not found")) ]
-- :}
-- {
-- "components": {
-- "schemas": {
-- "User": {
-- "type": "string"
-- }
-- }
-- },
-- "info": {
-- "title": "",
-- "version": ""
-- },
-- "openapi": "3.0.0",
-- "paths": {
-- "/user": {
-- "get": {
-- "responses": {
-- "200": {
-- "content": {
-- "application/json": {
-- "schema": {
-- "$ref": "#/components/schemas/User"
-- }
-- }
-- },
-- "description": "OK"
-- },
-- "404": {
-- "description": "User info not found"
-- }
-- }
-- }
-- }
-- }
-- }
--
-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
-- which produces @application/json@ output. It should respond with code @200@ and body specified
-- by schema @User@ which is defined in @'definitions'@ property of swagger specification.
-- Alternatively it may respond with code @404@ meaning that user info is not found.
--
-- For convenience, @swagger2@ uses /classy field lenses/. It means that
-- field accessor names can be overloaded for different types. One such
-- common field is @'description'@. Many components of a Swagger specification
-- can have descriptions, and you can use the same name for them:
--
-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & description .~ "No content"
-- {
-- "description": "No content"
-- }
-- >>> :{
-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
-- & type_ ?~ OpenApiBoolean
-- & description ?~ "To be or not to be"
-- :}
-- {
-- "description": "To be or not to be",
-- "type": "boolean"
-- }
--
-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
-- have direct access to it via @'at' code@. Example:
--
-- >>> :{
-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
-- & at 404 ?~ "Not found"
-- :}
-- {
-- "responses": {
-- "404": {
-- "description": "Not found"
-- }
-- }
-- }
--
-- You might've noticed that @'type_'@ has an extra underscore in its name
-- compared to, say, @'description'@ field accessor.
-- This is because @type@ is a keyword in Haskell.
-- A few other field accessors are modified in this way:
--
-- - @'in_'@, @'type_'@, @'default_'@ (as keywords);
-- - @'maximum_'@, @'minimum_'@, @'head_'@ (as conflicting with @Prelude@);
-- - @'enum_'@ (as conflicting with @Control.Lens@).
-- $schema
--
-- @'ParamSchema'@ and @'Schema'@ are the two core types for data model specification.
--
-- @'ParamSchema' t@ specifies all the common properties, available for every data schema.
-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'OpenApiType'@ and @'OpenApiItems'@).
--
-- @'Schema'@ is used for request and response bodies and allows specifying objects
-- with properties in addition to what @'ParamSchema'@ provides.
--
-- In most cases you will have a Haskell data type for which you would like to
-- define a corresponding schema. To facilitate this use case
-- @swagger2@ provides two classes for schema encoding.
-- Both these classes provide means to encode /types/ as Swagger /schemas/.
--
-- @'ToParamSchema'@ is intended to be used for primitive API endpoint parameters,
-- such as query parameters, headers and URL path pieces.
-- Its corresponding value-encoding class is @'ToHttpApiData'@ (from @http-api-data@ package).
--
-- @'ToSchema'@ is used for request and response bodies and mostly differ from
-- primitive parameters by allowing objects/mappings in addition to primitive types and arrays.
-- Its corresponding value-encoding class is @'ToJSON'@ (from @aeson@ package).
--
-- While lenses and prisms make it easy to define schemas, it might be that you don't need to:
-- @'ToSchema'@ and @'ToParamSchema'@ classes both have default @'Generic'@-based implementations!
--
-- @'ToSchema'@ default implementation is also aligned with @'ToJSON'@ default implementation with
-- the only difference being for sum encoding. @'ToJSON'@ defaults sum encoding to @'defaultTaggedObject'@,
-- while @'ToSchema'@ defaults to something which corresponds to @'ObjectWithSingleField'@. This is due to
-- @'defaultTaggedObject'@ behavior being hard to specify in Swagger.
--
-- Here's an example showing @'ToJSON'@–@'ToSchema'@ correspondance:
--
-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
-- >>> instance ToJSON Person
-- >>> instance ToSchema Person
-- >>> BSL.putStrLn $ encodePretty (Person "David" 28)
-- {
-- "age": 28,
-- "name": "David"
-- }
-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Person)
-- {
-- "properties": {
-- "age": {
-- "type": "integer"
-- },
-- "name": {
-- "type": "string"
-- }
-- },
-- "required": [
-- "name",
-- "age"
-- ],
-- "type": "object"
-- }
--
-- This package implements OpenAPI 3.0 spec, which supports @oneOf@ in schemas, allowing any sum types
-- to be faithfully represented. All sum encodings supported by @aeson@ are supported here as well, with
-- an exception of 'Data.Aeson.TwoElemArray', since OpenAPI spec does not support heterogeneous arrays.
--
-- An example with 'Data.Aeson.TaggedObject' encoding:
--
-- >>> data Error = ErrorNoUser { userId :: Int } | ErrorAccessDenied { requiredPermission :: String } deriving Generic
-- >>> instance ToJSON Error
-- >>> instance ToSchema Error
-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Error)
-- {
-- "oneOf": [
-- {
-- "properties": {
-- "tag": {
-- "enum": [
-- "ErrorNoUser"
-- ],
-- "type": "string"
-- },
-- "userId": {
-- "maximum": 9223372036854775807,
-- "minimum": -9223372036854775808,
-- "type": "integer"
-- }
-- },
-- "required": [
-- "userId",
-- "tag"
-- ],
-- "type": "object"
-- },
-- {
-- "properties": {
-- "requiredPermission": {
-- "type": "string"
-- },
-- "tag": {
-- "enum": [
-- "ErrorAccessDenied"
-- ],
-- "type": "string"
-- }
-- },
-- "required": [
-- "requiredPermission",
-- "tag"
-- ],
-- "type": "object"
-- }
-- ]
-- }
-- $manipulation
-- Sometimes you have to work with an imported or generated @'Swagger'@.
-- For instance, <servant-swagger http://hackage.haskell.org/package/servant-swagger> generates basic @'Swagger'@
-- for a type-level servant API.
--
-- Lenses and prisms can be used to manipulate such specification to add additional information, tags, extra responses, etc.
-- To facilitate common needs, @"Data.OpenApi.Operation"@ module provides useful helpers.
-- $validation
-- While @'ToParamSchema'@ and @'ToSchema'@ provide means to easily obtain schemas for Haskell types,
-- there is no static mechanism to ensure those instances correspond to the @'ToHttpApiData'@ or @'ToJSON'@ instances.
--
-- @"Data.OpenApi.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.