Merge pull request #639 from hudayou/swagger2

Provide a swagger2 spec for the dynamic API
This commit is contained in:
Joe Nelson
2016-06-20 15:12:58 -07:00
committed by GitHub
15 changed files with 2107 additions and 26 deletions
+2
View File
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
### Added
- Ability to generate an OpenAPI spec - @mainx07, @hudayou, @ruslantalpa, @begriffs
- Ability to set addresses to listen on - @hudayou
- Output names of used-defined types (instead of 'USER-DEFINED') - @martingms
+1 -1
View File
@@ -18,7 +18,7 @@ dependencies:
test:
override:
- stack test
- git ls-files | grep '\.l\?hs$' | xargs stack exec -- hlint -X QuasiQuotes "$@"
- git ls-files | grep '\.l\?hs$' | xargs stack exec -- hlint -X QuasiQuotes -X NoPatternSynonyms "$@"
- stack exec -- cabal update
- stack exec --no-ghc-package-path -- cabal install --only-d --dry-run
- stack exec -- packdeps *.cabal || true
+5 -2
View File
@@ -13,6 +13,7 @@ import PostgREST.DbStructure
import Control.Monad
import Data.Monoid ((<>))
import Data.String.Conversions (cs)
import Data.String (IsString (..))
import qualified Hasql.Query as H
import qualified Hasql.Session as H
import qualified Hasql.Decoders as HD
@@ -47,9 +48,11 @@ main = do
hSetBuffering stderr NoBuffering
conf <- readOptions
let port = configPort conf
let host = configHost conf
port = configPort conf
pgSettings = cs (configDatabase conf)
appSettings = setPort port
appSettings = setHost (fromString host)
. setPort port
. setServerName (cs $ "postgrest/" <> prettyVersion)
$ defaultSettings
+15 -6
View File
@@ -44,8 +44,8 @@ executable postgrest
, http-types
, interpolatedstring-perl6
, jwt
, microlens >= 0.4.2 && < 0.5
, microlens-aeson >= 2.1.1 && < 2.2
, lens >=3.8 && < 5.0
, lens-aeson >= 1.0.0.0 && < 1.1.0.0
, mtl
, optparse-applicative >= 0.11 && < 0.13
, parsec
@@ -65,6 +65,8 @@ executable postgrest
, wai-extra
, wai-middleware-static >= 0.6.0
, warp >= 3.1.0
, insert-ordered-containers >= 0.1.0.1
, swagger2 >= 2.1
, HTTP
, Ranged-sets
if !os(windows)
@@ -89,8 +91,8 @@ library
, http-types
, interpolatedstring-perl6
, jwt
, microlens
, microlens-aeson
, lens
, lens-aeson
, mtl
, optparse-applicative
, parsec
@@ -109,6 +111,8 @@ library
, wai-extra
, wai-middleware-static >= 0.6.0
, warp >= 3.1.0
, insert-ordered-containers >= 0.1.0.1
, swagger2 >= 2.1
Other-Modules: Paths_postgrest
Exposed-Modules: PostgREST.App
@@ -122,6 +126,7 @@ library
, PostgREST.RangeQuery
, PostgREST.ApiRequest
, PostgREST.Types
, PostgREST.OpenAPI
hs-source-dirs: src
Test-Suite spec
@@ -163,8 +168,8 @@ Test-Suite spec
, http-types
, interpolatedstring-perl6
, jwt
, microlens
, microlens-aeson
, lens
, lens-aeson
, monad-control
, mtl
, optparse-applicative
@@ -186,5 +191,9 @@ Test-Suite spec
, wai-extra
, wai-middleware-static
, warp
, insert-ordered-containers
, hjsonpointer
, hjsonschema
, swagger2
, HTTP
, Ranged-sets
+6 -1
View File
@@ -45,10 +45,11 @@ data Target = TargetIdent QualifiedIdentifier
data PreferRepresentation = Full | HeadersOnly | None deriving Eq
-- | Enumeration of currently supported content types for
-- route responses and upload payloads
data ContentType = ApplicationJSON | TextCSV deriving Eq
data ContentType = ApplicationJSON | TextCSV | OpenAPI deriving Eq
instance Show ContentType where
show ApplicationJSON = "application/json; charset=utf-8"
show TextCSV = "text/csv; charset=utf-8"
show OpenAPI = "application/openapi+json; charset=utf-8"
{-|
Describes what the user wants to do. This data type is a
@@ -123,6 +124,8 @@ userApiRequest schema req reqBody =
Nothing -> PayloadParseError "All lines must have same number of fields"
Just json -> PayloadJSON json)
(CSV.decodeByName reqBody)
Right oa@OpenAPI ->
PayloadParseError $ "Content-type not acceptable: " <> cs (show oa)
-- This is a Left value because form-urlencoded is not a content
-- type which we ever use for responses, only something we handle
-- just this once for requests
@@ -208,11 +211,13 @@ pickContentType :: Maybe BS.ByteString -> Either BS.ByteString ContentType
pickContentType accept
| isNothing accept || has ctAll || has ctJson = Right ApplicationJSON
| has ctCsv = Right TextCSV
| has ctOpenAPI = Right OpenAPI
| otherwise = Left accept'
where
ctAll = "*/*"
ctCsv = "text/csv"
ctJson = "application/json"
ctOpenAPI = "application/openapi+json"
Just accept' = accept
findInAccept = flip find $ parseHttpAccept accept'
has = isJust . findInAccept . BS.isPrefixOf
+22 -5
View File
@@ -60,6 +60,7 @@ import PostgREST.QueryBuilder ( callProc
, ResultsWithCount
)
import PostgREST.Types
import PostgREST.OpenAPI
import Prelude
@@ -180,9 +181,6 @@ app dbStructure conf apiRequest =
let cols = filter (filterCol tSchema tTable) $ dbColumns dbStructure
pkeys = map pkName $ filter (filterPk tSchema tTable) allPrKeys
body = encode (TableOptions cols pkeys)
filterCol :: Schema -> TableName -> Column -> Bool
filterCol sc tb Column{colTable=Table{tableSchema=s, tableName=t}} = s==sc && t==tb
filterCol _ _ _ = False
acceptH = (hAllow, if tableInsertable table then "GET,POST,PATCH,DELETE" else "GET") in
return $ responseLBS status200 [jsonH, allOrigins, acceptH] $ cs body
@@ -206,8 +204,13 @@ app dbStructure conf apiRequest =
else return notFound
(ActionRead, TargetRoot, Nothing) -> do
body <- encode <$> H.query schema accessibleTables
return $ responseLBS status200 [jsonH] $ cs body
let encodeApi ti = encodeOpenAPI ti host port
host = configHost conf
port = toInteger $ configPort conf
encodeFn = if contentType == OpenAPI then encodeApi . toTableInfo else encode
header = if contentType == OpenAPI then openapiH else jsonH
body <- encodeFn <$> H.query schema accessibleTables
return $ responseLBS status200 [header] $ cs body
(ActionInappropriate, _, _) -> return $ responseLBS status405 [] ""
@@ -220,8 +223,19 @@ app dbStructure conf apiRequest =
(_, _, _) -> return notFound
where
toTableInfo :: [Table] -> [(Table, [Column], [Text])]
toTableInfo = map (\t ->
let tSchema = tableSchema t
tTable = tableName t
cols = filter (filterCol tSchema tTable) $ dbColumns dbStructure
pkeys = map pkName $ filter (filterPk tSchema tTable) allPrKeys
in
(t, cols, pkeys))
notFound = responseLBS status404 [] ""
filterPk sc table pk = sc == (tableSchema . pkTable) pk && table == (tableName . pkTable) pk
filterCol :: Schema -> TableName -> Column -> Bool
filterCol sc tb Column{colTable=Table{tableSchema=s, tableName=t}} = s==sc && t==tb
filterCol _ _ _ = False
allPrKeys = dbPrimaryKeys dbStructure
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header
schema = cs $ configSchema conf
@@ -273,6 +287,9 @@ contentRangeH frm to total =
jsonH :: Header
jsonH = (hContentType, "application/json; charset=utf-8")
openapiH :: Header
openapiH = (hContentType, "application/openapi+json; charset=utf-8")
formatRelationError :: Text -> Text
formatRelationError = formatGeneralError
"could not find foreign keys between these entities"
+2 -2
View File
@@ -18,9 +18,9 @@ module PostgREST.Auth (
, tokenJWT
) where
import Lens.Micro
import Lens.Micro.Aeson
import Control.Lens
import Data.Aeson (Value (..), parseJSON, toJSON)
import Data.Aeson.Lens
import Data.Aeson.Types (parseMaybe, emptyObject, emptyArray)
import qualified Data.ByteString as BS
import qualified Data.Vector as V
+2
View File
@@ -39,6 +39,7 @@ data AppConfig = AppConfig {
configDatabase :: String
, configAnonRole :: String
, configSchema :: String
, configHost :: String
, configPort :: Int
, configJwtSecret :: Secret
, configPool :: Int
@@ -51,6 +52,7 @@ argParser = AppConfig
<$> argument str (help "(REQUIRED) database connection string, e.g. postgres://user:pass@host:port/db" <> metavar "DB_URL")
<*> strOption (long "anonymous" <> short 'a' <> help "(REQUIRED) postgres role to use for non-authenticated requests" <> metavar "ROLE")
<*> strOption (long "schema" <> short 's' <> help "schema to use for API routes" <> metavar "NAME" <> value "public" <> showDefault)
<*> strOption (long "host" <> short 'l' <> help "hostname or ip on which to run HTTP server" <> metavar "HOST" <> value "*4" <> showDefault)
<*> option auto (long "port" <> short 'p' <> help "port number on which to run HTTP server" <> metavar "PORT" <> value 3000 <> showDefault)
<*> (secret . cs <$>
strOption (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET" <> value "secret" <> showDefault))
+9 -4
View File
@@ -6,6 +6,7 @@ module PostgREST.Middleware where
import Data.Aeson (Value (..))
import qualified Data.HashMap.Strict as M
import Data.String.Conversions (cs)
import Data.Maybe (fromMaybe, listToMaybe)
import Data.Text
import qualified Hasql.Transaction as H
@@ -17,7 +18,7 @@ import Network.Wai.Middleware.Cors (cors)
import Network.Wai.Middleware.Gzip (def, gzip)
import Network.Wai.Middleware.Static (only, staticPolicy)
import PostgREST.ApiRequest (ApiRequest(..), pickContentType)
import PostgREST.ApiRequest (ApiRequest(..), ContentType(..), pickContentType)
import PostgREST.Auth (claimsToSQL)
import PostgREST.Config (AppConfig (..), corsPolicy)
import PostgREST.Error (errResponse)
@@ -40,10 +41,14 @@ runWithClaims conf eClaims app req =
unsupportedAccept :: Application -> Application
unsupportedAccept app req respond =
case accept of
Left _ -> respond $ errResponse status415 "Unsupported Accept header, try: application/json"
Right _ -> app req respond
case (isTargetRoot, accept) of
(_, Left _) -> unsupportedAcceptRespond
(False, Right OpenAPI) -> unsupportedAcceptRespond
(_, Right _) -> app req respond
where accept = pickContentType $ lookup hAccept $ requestHeaders req
path = pathInfo req
isTargetRoot = fromMaybe True $ (== "") <$> listToMaybe path
unsupportedAcceptRespond = respond $ errResponse status415 "Unsupported Accept header, try: application/json"
defaultMiddle :: Application -> Application
defaultMiddle =
+241
View File
@@ -0,0 +1,241 @@
{-# LANGUAGE OverloadedStrings #-}
module PostgREST.OpenAPI (
encodeOpenAPI
) where
import Control.Lens
import Data.Aeson (decode, encode)
import Data.ByteString.Lazy (ByteString)
import Data.HashMap.Strict.InsOrd (InsOrdHashMap, fromList)
import Data.String (IsString (..))
import Data.Text (Text, unpack, pack, concat, intercalate)
import qualified Data.Set as Set
import Prelude hiding (concat)
import Data.Swagger
import PostgREST.ApiRequest (ContentType(..))
import PostgREST.Config (prettyVersion)
import PostgREST.QueryBuilder (operators)
import PostgREST.Types (Table(..), Column(..))
makeMimeList :: [ContentType] -> MimeList
makeMimeList cs = MimeList $ map (fromString . show) cs
toSwaggerType :: Text -> SwaggerType t
toSwaggerType "text" = SwaggerString
toSwaggerType "integer" = SwaggerInteger
toSwaggerType "boolean" = SwaggerBoolean
toSwaggerType "numeric" = SwaggerNumber
toSwaggerType _ = SwaggerString
makeProperty :: Column -> (Text, Referenced Schema)
makeProperty c = (colName c, Inline u)
where
r = mempty :: Schema
s = if null $ colEnum c
then r
else r & enum_ .~ decode (encode (colEnum c))
t = s & type_ .~ toSwaggerType (colType c)
u = t & format ?~ colType c
makeProperties :: [Column] -> InsOrdHashMap Text (Referenced Schema)
makeProperties cs = fromList $ map makeProperty cs
makeDefinition :: (Table, [Column], [Text]) -> (Text, Schema)
makeDefinition (t, cs, _) =
let tn = tableName t in
(tn, (mempty :: Schema)
& type_ .~ SwaggerObject
& properties .~ makeProperties cs)
makeDefinitions :: [(Table, [Column], [Text])] -> InsOrdHashMap Text Schema
makeDefinitions ti = fromList $ map makeDefinition ti
makeOperatorPattern :: Text
makeOperatorPattern =
intercalate "|"
[ concat ["^", x, y, "[.]"] |
x <- ["not[.]", ""],
y <- map fst operators ]
makeRowFilter :: Column -> Param
makeRowFilter c =
(mempty :: Param)
& name .~ colName c
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamQuery
& type_ .~ SwaggerString
& format ?~ colType c
& pattern ?~ makeOperatorPattern)
makeRowFilters :: [Column] -> [Param]
makeRowFilters = map makeRowFilter
makeOrderItems :: [Column] -> [Text]
makeOrderItems cs =
[ concat [x, y, z] |
x <- map colName cs,
y <- [".asc", ".desc", ""],
z <- [".nullsfirst", ".nulllast", ""]
]
makeRangeParams :: [Param]
makeRangeParams =
[ (mempty :: Param)
& name .~ "Range"
& description ?~ "Limiting and Pagination"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamHeader
& type_ .~ SwaggerString)
, (mempty :: Param)
& name .~ "Range-Unit"
& description ?~ "Limiting and Pagination"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamHeader
& type_ .~ SwaggerString
& default_ .~ decode "\"items\"")
, (mempty :: Param)
& name .~ "offset"
& description ?~ "Limiting and Pagination"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamQuery
& type_ .~ SwaggerString)
, (mempty :: Param)
& name .~ "limit"
& description ?~ "Limiting and Pagination"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamQuery
& type_ .~ SwaggerString)
]
makePreferParam :: [Text] -> Param
makePreferParam ts =
(mempty :: Param)
& name .~ "Prefer"
& description ?~ "Preference"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamHeader
& type_ .~ SwaggerString
& enum_ .~ decode (encode ts))
makeSelectParam :: Param
makeSelectParam =
(mempty :: Param)
& name .~ "select"
& description ?~ "Filtering Columns"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamQuery
& type_ .~ SwaggerString)
makeGetParams :: [Column] -> [Param]
makeGetParams [] =
makeRangeParams ++
[ makeSelectParam
, makePreferParam ["plurality=singular", "count=none"]
]
makeGetParams cs =
makeRangeParams ++
[ makeSelectParam
, (mempty :: Param)
& name .~ "order"
& description ?~ "Ordering"
& required ?~ False
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
& in_ .~ ParamQuery
& type_ .~ SwaggerString
& enum_ .~ decode (encode $ makeOrderItems cs))
, makePreferParam ["plurality=singular", "count=none"]
]
makeReturnPreferenceParam :: Param
makeReturnPreferenceParam =
makePreferParam ["return=representation", "return=minimal", "return=none"]
makePostParams :: Text -> [Param]
makePostParams tn =
[ makeReturnPreferenceParam
, (mempty :: Param)
& name .~ "body"
& description ?~ tn
& required ?~ False
& schema .~ ParamBody (Ref (Reference tn))
]
makeDeleteParams :: [Param]
makeDeleteParams =
[ makeReturnPreferenceParam ]
makePathItem :: (Table, [Column], [Text]) -> (FilePath, PathItem)
makePathItem (t, cs, _) = ("/" ++ unpack tn, p $ tableInsertable t)
where
tOp = (mempty :: Operation)
& tags .~ Set.fromList [tn]
& produces ?~ makeMimeList [ApplicationJSON, TextCSV]
& at 200 ?~ "OK"
getOp = tOp
& parameters .~ map Inline (makeGetParams cs ++ rs)
& at 206 ?~ "Partial Content"
postOp = tOp
& consumes ?~ makeMimeList [ApplicationJSON, TextCSV]
& parameters .~ map Inline (makePostParams tn)
& at 201 ?~ "Created"
patchOp = tOp
& consumes ?~ makeMimeList [ApplicationJSON, TextCSV]
& parameters .~ map Inline (makePostParams tn ++ rs)
& at 204 ?~ "No Content"
deletOp = tOp
& parameters .~ map Inline (makeDeleteParams ++ rs)
pr = (mempty :: PathItem) & get ?~ getOp
pw = pr & post ?~ postOp & patch ?~ patchOp & delete ?~ deletOp
p False = pr
p True = pw
rs = makeRowFilters cs
tn = tableName t
makeRootPathItem :: (FilePath, PathItem)
makeRootPathItem = ("/", p)
where
getOp = (mempty :: Operation)
& tags .~ Set.fromList ["/"]
& produces ?~ makeMimeList [ApplicationJSON, OpenAPI]
& at 200 ?~ "OK"
pr = (mempty :: PathItem) & get ?~ getOp
p = pr
makePathItems :: [(Table, [Column], [Text])] -> InsOrdHashMap FilePath PathItem
makePathItems ti = fromList $ makeRootPathItem : map makePathItem ti
escapeHostName :: String -> String
escapeHostName "*" = "0.0.0.0"
escapeHostName "*4" = "0.0.0.0"
escapeHostName "!4" = "0.0.0.0"
escapeHostName "*6" = "0.0.0.0"
escapeHostName "!6" = "0.0.0.0"
escapeHostName h = h
postgrestSpec:: [(Table, [Column], [Text])] -> String -> Integer -> Swagger
postgrestSpec ti h p = (mempty :: Swagger)
& basePath ?~ "/"
& schemes ?~ [Http]
& info .~ ((mempty :: Info)
& version .~ pack prettyVersion
& title .~ "PostgREST API"
& description ?~ "This is a dynamic API generated by PostgREST")
& host .~ h'
& definitions .~ makeDefinitions ti
& paths .~ makePathItems ti
where
h' = Just $ Host (escapeHostName h) (Just (fromInteger p))
encodeOpenAPI :: [(Table, [Column], [Text])] -> String -> Integer -> ByteString
encodeOpenAPI ti h p = encode $ postgrestSpec ti h p
+9 -1
View File
@@ -16,9 +16,17 @@ extra-deps:
- wai-cors-0.2.5
- cryptohash-sha256-0.11.100.0
- hackage-security-0.5.2.1
- unordered-containers-0.2.7.1
- insert-ordered-containers-0.1.0.1
- swagger2-2.1
# - hjsonschema-0.10.0.2
- hjsonpointer-0.3.0.1
ghc-options:
postgrest: -O2 -Werror -Wall -fwarn-identities
packages:
- .
- location:
git: https://github.com/seagreen/hjsonschema
commit: 075da33626d9d5cf20645a998b86518222d6b2d6
extra-dep: true
+32 -1
View File
@@ -8,7 +8,11 @@ import SpecHelper
import Network.HTTP.Types
import Network.Wai (Application)
import Network.Wai.Test (SResponse(simpleHeaders))
import Network.Wai.Test (SResponse(simpleStatus, simpleHeaders, simpleBody))
import Data.Maybe (fromJust)
import Data.Aeson (decode)
import qualified Data.JsonSchema.Draft4 as D4
spec :: SpecWith Application
spec = do
@@ -61,6 +65,33 @@ spec = do
] |]
{matchStatus = 200}
it "returns a valid openapi spec" $ do
r <- request methodGet "/" [("Accept", "application/openapi+json")] ""
liftIO $
let respStatus = simpleStatus r in
respStatus `shouldSatisfy`
\s -> s == Status { statusCode = 200, statusMessage="OK" }
liftIO $
let respHeaders = simpleHeaders r in
respHeaders `shouldSatisfy`
\hs -> ("Content-Type", "application/openapi+json; charset=utf-8") `elem` hs
liftIO $
let respBody = simpleBody r
schema :: D4.Schema
schema = D4.emptySchema { D4._schemaRef = Just "openapi.json" }
schemaContext :: D4.SchemaWithURI D4.Schema
schemaContext = D4.SchemaWithURI
{ D4._swSchema = schema
, D4._swURI = Just "test/fixtures/openapi.json"
}
in
D4.fetchFilesystemAndValidate schemaContext ((fromJust . decode) respBody) `shouldReturn` Right ()
it "should respond to openapi request on none root path with 415" $
request methodGet "/none_root_path"
(acceptHdrs "application/openapi+json") ""
`shouldRespondWith` 415
describe "Table info" $ do
it "The structure of complex views is correctly detected" $
request methodOptions "/filtered_tasks" [] "" `shouldRespondWith`
+3 -3
View File
@@ -19,15 +19,15 @@ testDbConn = "postgres://postgrest_test_authenticator@localhost:5432/postgrest_t
testCfg :: AppConfig
testCfg =
AppConfig testDbConn "postgrest_test_anonymous" "test" 3000 (secret "safe") 10 Nothing True
AppConfig testDbConn "postgrest_test_anonymous" "test" "localhost" 3000 (secret "safe") 10 Nothing True
testUnicodeCfg :: AppConfig
testUnicodeCfg =
AppConfig testDbConn "postgrest_test_anonymous" "تست" 3000 (secret "safe") 10 Nothing True
AppConfig testDbConn "postgrest_test_anonymous" "تست" "localhost" 3000 (secret "safe") 10 Nothing True
testLtdRowsCfg :: AppConfig
testLtdRowsCfg =
AppConfig testDbConn "postgrest_test_anonymous" "test" 3000 (secret "safe") 10 (Just 2) True
AppConfig testDbConn "postgrest_test_anonymous" "test" "localhost" 3000 (secret "safe") 10 (Just 2) True
setupDb :: IO ()
setupDb = do
+151
View File
@@ -0,0 +1,151 @@
{
"id": "draft04.json",
"$schema": "draft04.json",
"description": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#" }
},
"positiveInteger": {
"type": "integer",
"minimum": 0
},
"positiveIntegerDefault0": {
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
},
"simpleTypes": {
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
},
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uri"
},
"$schema": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": {},
"multipleOf": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "boolean",
"default": false
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": false
},
"maxLength": { "$ref": "#/definitions/positiveInteger" },
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
},
"maxItems": { "$ref": "#/definitions/positiveInteger" },
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
"required": { "$ref": "#/definitions/stringArray" },
"additionalProperties": {
"anyOf": [
{ "type": "boolean" },
{ "$ref": "#" }
],
"default": {}
},
"definitions": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"dependencies": {
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/stringArray" }
]
}
},
"enum": {
"type": "array",
"minItems": 1,
"uniqueItems": true
},
"type": {
"anyOf": [
{ "$ref": "#/definitions/simpleTypes" },
{
"type": "array",
"items": { "$ref": "#/definitions/simpleTypes" },
"minItems": 1,
"uniqueItems": true
}
]
},
"format": { "type": "string" },
"allOf": { "$ref": "#/definitions/schemaArray" },
"anyOf": { "$ref": "#/definitions/schemaArray" },
"oneOf": { "$ref": "#/definitions/schemaArray" },
"not": { "$ref": "#" }
},
"dependencies": {
"exclusiveMaximum": [ "maximum" ],
"exclusiveMinimum": [ "minimum" ]
},
"default": {}
}
+1607
View File
File diff suppressed because it is too large Load Diff