git.iko.soy/typeable/swagger2
on percent-encoding-2.3.1.1 help sign in
345 lines 13 kB history raw
1{-# LANGUAGE CPP #-}
2-- |
3-- Module: Data.Swagger
4-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
5-- Stability: experimental
6--
7-- Swagger™ is a project used to describe and document RESTful APIs.
8--
9-- The Swagger specification defines a set of files required to describe such an API.
10-- These files can then be used by the Swagger-UI project to display the API
11-- and Swagger-Codegen to generate clients in various languages.
12-- Additional utilities can also take advantage of the resulting files, such as testing tools.
13module Data.Swagger (
14 -- * How to use this library
15 -- $howto
17 -- ** @'Monoid'@ instances
18 -- $monoids
20 -- ** Lenses and prisms
21 -- $lens
23 -- ** Schema specification
24 -- $schema
26 -- ** Manipulation
27 -- $manipulation
29 -- ** Validation
30 -- $validation
32 -- * Re-exports
33 module Data.Swagger.Lens,
34 module Data.Swagger.Operation,
35 module Data.Swagger.ParamSchema,
36 module Data.Swagger.Schema,
37 module Data.Swagger.Schema.Validation,
39 -- * Swagger specification
40 Swagger(..),
41 Host(..),
42 Scheme(..),
44 -- ** Info types
45 Info(..),
46 Contact(..),
47 License(..),
49 -- ** PathItem
50 PathItem(..),
52 -- ** Operations
53 Operation(..),
54 Tag(..),
55 TagName,
57 -- ** Types and formats
58 SwaggerType(..),
59 Format,
60 Definitions,
61 CollectionFormat(..),
63 -- ** Parameters
64 Param(..),
65 ParamAnySchema(..),
66 ParamOtherSchema(..),
67 ParamLocation(..),
68 ParamName,
69 Header(..),
70 HeaderName,
71 Example(..),
73 -- ** Schemas
74 ParamSchema(..),
75 Schema(..),
76 NamedSchema(..),
77 SwaggerItems(..),
78 Xml(..),
79 Pattern,
80 AdditionalProperties(..),
82 -- ** Responses
83 Responses(..),
84 Response(..),
85 HttpStatusCode,
87 -- ** Security
88 SecurityScheme(..),
89 SecuritySchemeType(..),
90 SecurityRequirement(..),
92 -- *** API key
93 ApiKeyParams(..),
94 ApiKeyLocation(..),
96 -- *** OAuth2
97 OAuth2Params(..),
98 OAuth2Flow(..),
99 AuthorizationURL,
100 TokenURL,
102 -- ** External documentation
103 ExternalDocs(..),
105 -- ** References
106 Reference(..),
107 Referenced(..),
109 -- ** Miscellaneous
110 MimeList(..),
111 URL(..),
112) where
114import Data.Swagger.Lens
115import Data.Swagger.Operation
116import Data.Swagger.ParamSchema
117import Data.Swagger.Schema
118import Data.Swagger.Schema.Validation
120import Data.Swagger.Internal
122-- $setup
123-- >>> import Control.Lens
124-- >>> import Data.Aeson
125-- >>> import Data.Monoid
126-- >>> import Data.Proxy
127-- >>> import GHC.Generics
128-- >>> :set -XDeriveGeneric
129-- >>> :set -XOverloadedStrings
130-- >>> :set -XOverloadedLists
131-- >>> :set -fno-warn-missing-methods
133-- $howto
134--
135-- This section explains how to use this library to work with Swagger specification.
137-- $monoids
138--
139-- Virtually all types representing Swagger specification have @'Monoid'@ instances.
140-- The @'Monoid'@ type class provides two methods — @'mempty'@ and @'mappend'@.
141--
142-- In this library you can use @'mempty'@ for a default/empty value. For instance:
143--
144-- >>> encode (mempty :: Swagger)
145-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"}}"
146--
147-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
148-- That is because these properties are actually required ones.
149--
150-- You /should/ always override the default (empty) value for these properties,
151-- although it is not strictly necessary:
152--
153-- >>> encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
154-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
155--
156-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
157--
158-- >>> encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
159-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
160--
161-- This can be useful for combining specifications of endpoints into a whole API specification:
162--
163-- @
164-- \-\- /account subAPI specification
165-- accountAPI :: Swagger
166--
167-- \-\- /task subAPI specification
168-- taskAPI :: Swagger
169--
170-- \-\- while API specification is just a combination
171-- \-\- of subAPIs' specifications
172-- api :: Swagger
173-- api = accountAPI <> taskAPI
174-- @
176-- $lens
177--
178-- Since @'Swagger'@ has a fairly complex structure, lenses and prisms are used
179-- to work comfortably with it. In combination with @'Monoid'@ instances, lenses
180-- make it fairly simple to construct/modify any part of the specification:
181--
182-- >>> :{
183-- encode $ (mempty :: Swagger)
184-- & definitions .~ [ ("User", mempty & type_ .~ SwaggerString) ]
185-- & paths .~
186-- [ ("/user", mempty & get ?~ (mempty
187-- & produces ?~ MimeList ["application/json"]
188-- & at 200 ?~ ("OK" & _Inline.schema ?~ Ref (Reference "User"))
189-- & at 404 ?~ "User info not found")) ]
190-- :}
191-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"produces\":[\"application/json\"],\"responses\":{\"404\":{\"description\":\"User info not found\"},\"200\":{\"schema\":{\"$ref\":\"#/definitions/User\"},\"description\":\"OK\"}}}}},\"definitions\":{\"User\":{\"type\":\"string\"}}}"
192--
193-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
194-- which produces @application/json@ output. It should respond with code @200@ and body specified
195-- by schema @User@ which is defined in @'definitions'@ property of swagger specification.
196-- Alternatively it may respond with code @404@ meaning that user info is not found.
197--
198-- For convenience, @swagger2@ uses /classy field lenses/. It means that
199-- field accessor names can be overloaded for different types. One such
200-- common field is @'description'@. Many components of a Swagger specification
201-- can have descriptions, and you can use the same name for them:
202--
203-- >>> encode $ (mempty :: Response) & description .~ "No content"
204-- "{\"description\":\"No content\"}"
205-- >>> :{
206-- encode $ (mempty :: Schema)
207-- & type_ .~ SwaggerBoolean
208-- & description ?~ "To be or not to be"
209-- :}
210-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
211--
212-- @'ParamSchema'@ is basically the /base schema specification/ and many types contain it (see @'HasParamSchema'@).
213-- So for convenience, all @'ParamSchema'@ fields are transitively made fields of the type that has it.
214-- For example, you can use @'type_'@ to access @'SwaggerType'@ of @'Header'@ schema without having to use @'paramSchema'@:
215--
216-- >>> encode $ (mempty :: Header) & type_ .~ SwaggerNumber
217-- "{\"type\":\"number\"}"
218--
219-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
220-- have direct access to it via @'at' code@. Example:
221--
222-- >>> :{
223-- encode $ (mempty :: Operation)
224-- & at 404 ?~ "Not found"
225-- :}
226-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
227--
228-- You might've noticed that @'type_'@ has an extra underscore in its name
229-- compared to, say, @'description'@ field accessor.
230-- This is because @type@ is a keyword in Haskell.
231-- A few other field accessors are modified in this way:
232--
233-- - @'in_'@, @'type_'@, @'default_'@ (as keywords);
234-- - @'maximum_'@, @'minimum_'@, @'head_'@ (as conflicting with @Prelude@);
235-- - @'enum_'@ (as conflicting with @Control.Lens@).
237-- $schema
238--
239-- @'ParamSchema'@ and @'Schema'@ are the two core types for data model specification.
240--
241-- @'ParamSchema' t@ specifies all the common properties, available for every data schema.
242-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'SwaggerType'@ and @'SwaggerItems'@).
243--
244-- @'Schema'@ is used for request and response bodies and allows specifying objects
245-- with properties in addition to what @'ParamSchema'@ provides.
246--
247-- In most cases you will have a Haskell data type for which you would like to
248-- define a corresponding schema. To facilitate this use case
249-- @swagger2@ provides two classes for schema encoding.
250-- Both these classes provide means to encode /types/ as Swagger /schemas/.
251--
252-- @'ToParamSchema'@ is intended to be used for primitive API endpoint parameters,
253-- such as query parameters, headers and URL path pieces.
254-- Its corresponding value-encoding class is @'ToHttpApiData'@ (from @http-api-data@ package).
255--
256-- @'ToSchema'@ is used for request and response bodies and mostly differ from
257-- primitive parameters by allowing objects/mappings in addition to primitive types and arrays.
258-- Its corresponding value-encoding class is @'ToJSON'@ (from @aeson@ package).
259--
260-- While lenses and prisms make it easy to define schemas, it might be that you don't need to:
261-- @'ToSchema'@ and @'ToParamSchema'@ classes both have default @'Generic'@-based implementations!
262--
263-- @'ToSchema'@ default implementation is also aligned with @'ToJSON'@ default implementation with
264-- the only difference being for sum encoding. @'ToJSON'@ defaults sum encoding to @'defaultTaggedObject'@,
265-- while @'ToSchema'@ defaults to something which corresponds to @'ObjectWithSingleField'@. This is due to
266-- @'defaultTaggedObject'@ behavior being hard to specify in Swagger.
267--
268-- Here's an example showing @'ToJSON'@–@'ToSchema'@ correspondance:
269--
270-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
271-- >>> instance ToJSON Person
272-- >>> instance ToSchema Person
273-- >>> encode (Person "David" 28)
274-- "{\"age\":28,\"name\":\"David\"}"
275-- >>> encode $ toSchema (Proxy :: Proxy Person)
276-- "{\"required\":[\"name\",\"age\"],\"properties\":{\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"}},\"type\":\"object\"}"
277--
278-- Please note that not all valid Haskell data types will have a proper swagger schema. For example while we can derive a
279-- schema for basic enums like
280--
281-- >>> data SampleEnum = ChoiceOne | ChoiceTwo deriving Generic
282-- >>> instance ToSchema SampleEnum
283-- >>> instance ToJSON SampleEnum
284--
285-- and for sum types that have constructors with values
286--
287-- >>> data SampleSumType = ChoiceInt Int | ChoiceString String deriving Generic
288-- >>> instance ToSchema SampleSumType
289-- >>> instance ToJSON SampleSumType
290--
291-- we can not derive a valid schema for a mix of the above. The following will result in a type error
292--
293#if __GLASGOW_HASKELL__ < 800
294-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
295-- >>> instance ToSchema BadMixedType
296-- ...
297-- ... error:
298-- ... • No instance for (Data.Swagger.Internal.TypeShape.CannotDeriveSchemaForMixedSumType
299-- ... BadMixedType)
300-- ... arising from a use of ‘Data.Swagger.Internal.Schema.$dmdeclareNamedSchema’
301-- ... • In the expression:
302-- ... Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
303-- ... In an equation for ‘declareNamedSchema’:
304-- ... declareNamedSchema
305-- ... = Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
306-- ... In the instance declaration for ‘ToSchema BadMixedType’
307#else
308-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
309-- >>> instance ToSchema BadMixedType
310-- ...
311-- ... error:
312-- ... • Cannot derive Generic-based Swagger Schema for BadMixedType
313-- ... BadMixedType is a mixed sum type (has both unit and non-unit constructors).
314-- ... Swagger does not have a good representation for these types.
315-- ... Use genericDeclareNamedSchemaUnrestricted if you want to derive schema
316-- ... that matches aeson's Generic-based toJSON,
317-- ... but that's not supported by some Swagger tools.
318-- ... • In the expression:
319-- ... Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
320-- ... In an equation for ‘declareNamedSchema’:
321-- ... declareNamedSchema
322-- ... = Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
323-- ... In the instance declaration for ‘ToSchema BadMixedType’
324#endif
325--
326-- We can use 'genericDeclareNamedSchemaUnrestricted' to try our best to represent this type as a Swagger Schema and match 'ToJSON':
327--
328-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
329-- >>> instance ToSchema BadMixedType where declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
330-- >>> instance ToJSON BadMixedType
331--
333-- $manipulation
334-- Sometimes you have to work with an imported or generated @'Swagger'@.
335-- For instance, <servant-swagger http://hackage.haskell.org/package/servant-swagger> generates basic @'Swagger'@
336-- for a type-level servant API.
337--
338-- Lenses and prisms can be used to manipulate such specification to add additional information, tags, extra responses, etc.
339-- To facilitate common needs, @"Data.Swagger.Operation"@ module provides useful helpers.
341-- $validation
342-- While @'ToParamSchema'@ and @'ToSchema'@ provide means to easily obtain schemas for Haskell types,
343-- there is no static mechanism to ensure those instances correspond to the @'ToHttpApiData'@ or @'ToJSON'@ instances.
344--
345-- @"Data.Swagger.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.