git.iko.soy/typeable/swagger2
on master help sign in
add a quickcheck generator for aeson Values that match a swagger schema
ce7422cBen Weitzman
src/Data/Swagger/Schema/Generator.hsnew file+79
@@ -0,0 +1,79 @@
1+{-# LANGUAGE GADTs #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+
4+module Data.Swagger.Schema.Generator where
5+
6+import Control.Lens.Operators
7+import Control.Monad
8+import Data.Aeson
9+import Data.Aeson.Types
10+import qualified Data.HashMap.Strict.InsOrd as M
11+import Data.Maybe
12+import Data.Proxy
13+import Data.Scientific
14+import Data.Swagger
15+import Data.Swagger.Declare
16+import qualified Data.Text as T
17+import qualified Data.Vector as V
18+import Test.QuickCheck (arbitrary)
19+import Test.QuickCheck.Gen
20+import Test.QuickCheck.Property
21+
22+schemaGen :: Definitions Schema -> Schema -> Gen Value
23+schemaGen _ schema
24+ | Just cases <- schema ^. paramSchema . enum_ = elements cases
25+schemaGen defns schema =
26+ case schema ^. type_ of
27+ SwaggerBoolean -> Bool <$> elements [True, False]
28+ SwaggerNull -> pure Null
29+ SwaggerNumber
30+ | Just min <- schema ^. minimum_
31+ , Just max <- schema ^. maximum_ ->
32+ Number . fromFloatDigits <$>
33+ choose (toRealFloat min, toRealFloat max :: Double)
34+ | otherwise -> Number .fromFloatDigits <$> (arbitrary :: Gen Double)
35+ SwaggerInteger
36+ | Just min <- schema ^. minimum_
37+ , Just max <- schema ^. maximum_ ->
38+ Number . fromInteger <$>
39+ choose (truncate min, truncate max)
40+ | otherwise -> Number . fromInteger <$> arbitrary
41+ SwaggerArray
42+ | Just 0 <- schema ^. maxLength -> pure $ Array V.empty
43+ | Just items <- schema ^. items ->
44+ case items of
45+ SwaggerItemsObject ref ->
46+ let itemSchema = dereference defns ref
47+ in fmap (Array . V.fromList) . listOf $ schemaGen defns itemSchema
48+ SwaggerItemsArray refs ->
49+ let itemGens = schemaGen defns . dereference defns <$> refs
50+ in fmap (Array . V.fromList) $ sequence itemGens
51+ SwaggerString -> String . T.pack <$> arbitrary
52+ SwaggerObject -> do
53+ let props = dereference defns <$> schema ^. properties
54+ reqKeys = schema ^. required
55+ allKeys = M.keys $ schema ^. properties
56+ presentKeys <- filterM (\key -> if key `elem` reqKeys
57+ then return True
58+ else arbitrary) allKeys
59+ let presentProps = M.filterWithKey (\k _ -> k `elem` presentKeys) props
60+ let gens = schemaGen defns <$> presentProps
61+ x <- sequence gens
62+ return . Object $ M.toHashMap x
63+ where
64+ dereference :: Definitions a -> Referenced a -> a
65+ dereference _ (Inline a) = a
66+ dereference defs (Ref (Reference ref)) = fromJust $ M.lookup ref defs
67+
68+genValue :: (ToSchema a) => Proxy a -> Gen Value
69+genValue p =
70+ let (defs, NamedSchema _ schema) = runDeclare (declareNamedSchema p) M.empty
71+ in schemaGen defs schema
72+
73+validateFromJSON :: forall a . (ToSchema a, FromJSON a) => Proxy a -> Property
74+validateFromJSON p = forAll (genValue p) $
75+ \val -> case parseEither parseJSON val of
76+ Right (_ :: a) -> succeeded
77+ Left err -> failed
78+ { reason = err
79+ }
swagger2.cabal+3−1
@@ -39,6 +39,7 @@ library
39 Data.Swagger.Operation
39 Data.Swagger.Operation
40 Data.Swagger.ParamSchema
40 Data.Swagger.ParamSchema
41 Data.Swagger.Schema
41 Data.Swagger.Schema
42+ Data.Swagger.Schema.Generator
42 Data.Swagger.Schema.Validation
43 Data.Swagger.Schema.Validation
43 Data.Swagger.SchemaOptions
44 Data.Swagger.SchemaOptions
44
45
@@ -79,6 +80,7 @@ library
79 , unordered-containers >=0.2.9.0 && <0.3
80 , unordered-containers >=0.2.9.0 && <0.3
80 , uuid-types >=1.0.2 && <1.1
81 , uuid-types >=1.0.2 && <1.1
81 , vector >=0.12.0.1 && <0.13
82 , vector >=0.12.0.1 && <0.13
83+ , QuickCheck >=2.10.1 && <2.12
82
84
83 default-language: Haskell2010
85 default-language: Haskell2010
84
86
@@ -89,7 +91,7 @@ test-suite spec
89
91
90 -- From library parat
92 -- From library parat
91 -- We need aeson's toEncoding for doctests too
93 -- We need aeson's toEncoding for doctests too
92− build-depends:
94+ build-depends:
93 base
95 base
94 , swagger2
96 , swagger2
95 , aeson
97 , aeson