JSON-Validator-5.18/ 0000755 0000765 0000024 00000000000 15211411470 014026 5 ustar jhthorsen staff JSON-Validator-5.18/Changes 0000644 0000765 0000024 00000061227 15211411444 015332 0 ustar jhthorsen staff Revision history for perl distribution JSON-Validator
5.18 2026-06-08T09:53:00
- Bundle OpenAPI v3.0 schema 2021-09-28 as default #295, #286
Contributor: Henrik Andersen
- Fix nondeterministic false errors from the recursive_data_protection cache #272, #294
Contributor: Henrik Andersen
5.17 2026-06-04T22:32:00
- Add validate Mojo::Upload as binary string in OpenAPIv3 #288
Contributor: Archey Barrell
5.16 2026-06-04T10:06:00
- Use Mojo::File::spew instead of Mojo::File::spurt #281
Contributor: Emmanuel Seyman
- Updated dependency list #283
- Use negotiated content-type to select schema #289
Contributor: Archey Barrell
- Updated idn-email format verification to allow periods in local part #290
Contributor: Keith Carangelo
- Add string as valid format #293
5.15 2025-03-16T18:47:47
- Make JSON::Validator::Util::is_bool return true when passed perl v5.36+ builtin booleans #275
- Fix wrong resolving of responses component using $ref #277
- Fix array coercion for array parameters with a $ref schema #274
5.14 2023-03-06T13:45:33
- Avoid mutating OpenAPIv2 schema "parameters" when validating
5.13 2022-12-09T09:55:24
- Fix not coercing body parameter for OpenAPIv2
5.12 2022-10-26T20:14:57
- Allow unicode strings in the JSON schema #268
Contributor: Brad Barden
- Specified Perl version
- Updated basic repository files
- Updated contributors list
5.11 2022-08-30T15:41:16+0900
- Fix sorting routes with placeholders on different positions
5.10 2022-08-18T07:39:15+0900
- Add root schema id to "DefaultResponse" to allow external $ref
5.09 2022-08-17T09:13:55+0900
- Add support for default collectionFormat when "type" is "array"
5.08 2022-03-25T10:00:18+0900
- Fix handling OpenAPIv2 headers with collectionFormat
5.07 2022-03-23T07:58:02+0900
- Fix issues with OpenAPIv3 in 5.06
5.06 2022-03-23T07:29:23+0900
- Fix placing OpenAPIv2 bundled path $ref's inside "paths"
5.05 2022-01-06T09:33:30+0900
- Add EXPERIMENTAL support for coercing to array when posting form data
5.04 2021-12-10T07:54:09+0900
- Fix coercing strings and numbers in schema specification #266
5.03 2021-11-20T13:32:42+0900
- Fix OpenAPIv3::add_default_response() will not overwrite nested $ref
- Fix finding "readOnly" and "writeOnly" inside nested $ref
- Fix joi required() with nested objects
Contributor: Tim Stallard
5.02 2021-10-06T09:58:55+0900
- Fix defaults for arrays with collectionFormat #263
Contributor: Ilya Rassadin
- Improved documentation for JSON::Validator and JSON::Validator::Schema #230
5.01 2021-10-04T07:13:32+0900
- Fix registering the correct $ref when bundling
- Changed get() to resolve $ref #262
- Deprecated $schema->contains()
- Deprecated $schema->schema()
5.00 2021-10-02T10:15:16+0900
- Validation is much faster
- Fix "$defs" in draft7 is "definitions"
- Add benchmark.t to track validation speed
* Runtime v4.21: 2.62s (n=200)
* Runtime v5.00: 1.52s (n=200)
- Add JSON::Validator::Store::resolve()
- Add JSON::Validator::URI
- Fix incorrect validation error paths when validating references to same object #244
- Changed JSON::Validator::Util::is_type()
* Add JSON::Validator::Util::is_bool()
* Add JSON::Validator::Util::is_num()
- Changed bundled definitions/$defs names
- Changed invalid schema id/$id will not croak
- Changed internal _validation_xxx() methods to take $state
- Removed support for bundle({replace => 1}) #194
- Removed JSON::Validator::OpenAPIv2::allow_invalid_ref()
- Removed JSON::Validator::Ref
- Removed JSON::Validator::Util::json_pointer()
- Removed JSON::Validator::Util::schema_extract()
4.25 2021-09-26T08:41:51+0900
- Fix checking OpenAPI schema for errors if coerce(defaults) is on #254
- YAML::XS is preferred instead of YAML::PP #259
- Cleaned up CPAN distribution files #260
4.24 2021-09-16T08:02:26+0200
- Add OpenAPI v3.1.x support
4.23 2021-08-28T16:44:05+0200
- Fix mishandling of nullable combined witn enum in OpenAPIv3 #256 #257
4.22 2021-08-27T13:12:56+0200
- Fix email checking #258
Contributor: Stephan Hradek
4.21 2021-07-10T15:58:47+0900
- Fix no warnings when negotiating */* #252
- Depends on perl v5.16.0 #250
- Updated x_IRC to irc.libera.chat
4.20 2021-06-18T11:27:02+0900
- Fix compiling array items #249
- Fix coerce() will be passed on to schema() objects
- Add is_invalid() to JSON::Validator::Schema
- Improved documentation for JSON::Validator and JSON::Validator::Schema
- Changed joi validator to default to a JSON::Validator::Schema::Draft7 object
- Deprecated validate(..., $schema)
- Removed support for coerce(1)
- Removed support for subclassing JSON::Validator. Need to subclass a schema
class instead.
- Simpler inheritance for Schema classes
4.19 2021-06-17T12:37:10+0900
- Fix generating correct base_url() object with host and port
4.18 2021-06-17T11:13:58+0900
- Fix content_type validation for OpenAPIv2 and OpenAPIv3
- Fix validating multipart\/form-data with boundary
- Fix validating content-type with charset
- Fix parsing OpenAPIv3 spec with "description", "servers" and "summary" in a
path specifiation.
4.17 2021-04-28T11:30:56+0900
- Add add_default_response() to OpenAPIv2 and OpenAPIv3
- Add base_url() to OpenAPIv2 and OpenAPIv3
- Fix validating "nullable" for "array" and "object"
4.16 2021-03-24T08:57:46+0900
- Fix handling OpenAPIv2 "responses" $ref when bundling
4.15 2021-03-24T07:57:58+0900
- Fix validating nullable inside anyOf for OpenAPIv3 #241
- Fix t/id-keyword-draft*.t test failures with Mojolicious 9.11 #242 #243
- Removed deprecated functions joi() and validate_json()
- Removed deprecated methods singleton() and version()
4.14 2021-02-23T14:58:07+0900
- Add routes() to Schema::OpenAPIv2 and OpenAPIv3
4.13 2021-01-28T18:22:43+0900
- Fix handling offset in RFC3339 date-time #236
- Add CLEAR method to JSON::Validator::Ref #237
4.12 2021-01-25T07:52:34+0900
- Fix not using Mojo::Exception::raise() #235
- Fix uninitialized warning when looking up schema for an internal $ref
4.11 2021-01-24T16:02:02+0900
- Add JSON::Validator::Schema::OpenAPIv2
* Extends JSON::Validator::Schema::Draft4
* Can validate OpenAPIv2 API specifiation
* Can validate HTTP request and response
* Can validate "Accept" and "Content-Type"
* Can handle "discriminator"
* Can handle "readOnly" parameters
* Can handle collectionFormat
* Can handle default values for parameters
* Can convert specifiation with invalid "$ref" into a valid OpenAPIv2 specifiation
* Will coerce query parameters and headers into arrays if needed
- Add JSON::Validator::Schema::OpenAPIv3
* Extends JSON::Validator::Schema::Draft201909
* Can validate OpenAPIv3 API specifiation
* Can validate HTTP request and response
* Can validate "Accept" and "Content-Type"
* Can handle "discriminator"
* Can handle "nullable" parameters
* Can handle "readOnly" parameters
* Can handle "style" and "explode" for arrays and objects parameters
* Can handle "writeOnly" parameters
* Can handle default values for parameters
* Will coerce query parameters and headers into arrays if needed
- Add negotiate_content_type() utility function
- Fix t/load-file.t on Windows #234
- Fix not checking if input schema is a file if it has a newline #223 #233
Contributor: David Cantrell
- Improved error message when loading non-existing file #231
4.10 2020-10-13T10:53:11+0900
- Add JSON::Validator::Store
- Changed JSON::Validator->schema() to also load
- JSON::Validator->schema() can handle draft 2019-09 schemas
- Documented what is not supported in draft 2019-09
4.09 2020-10-12T11:04:16+0900
- Fix handling if "id" and "$id" in subschemas #186
- Add CONTRIBUTING.md #161
4.08 2020-10-12T08:06:29+0900
- Implemented basic support for draft 2019-09 #181
- JSON::Validator::Ref can hold sibling attrs #181
4.07 2020-10-11T11:48:05+0900
- Acceptance tests require Test::JSON::Schema::Acceptance 1.000
4.06 2020-10-10T16:21:51+0900
- Fix caching of schemas across objects #204 #212
Contributor: Karen Etheridge
- Fix "contains" can also be boolean "false"
- Fix "dependencies" can have boolean subschemas
- Fix "if" can be false
- Fix "null" is valid if "pattern" is defined, but no "type"
- Fix "propertyNames" can be false
- Fix JSON::Validator::Ref can hold a boolean schema
- Fix not adding "properties" to the input schema
- Fix resolving "$ref":"#id"
- Fix validating data when "additionalItems" is false, but there are enough rules
- Changed internals of JSON::Validator::Ref
4.05 2020-10-06T06:29:31+0900
- Errors are now reported in a predictable order #223
Contributor: David Cantrell
4.04 2020-09-28T10:01:58+0900
- Add "validator" as attribute to JSON::Validator::Joi
- Changed YAML::XS to an optional module #205
- Changed column with to be in sync with mojolicious/mojo
- Fix constructing schema() from correct class
4.03 2020-09-14T18:14:19+0900
- Add "joi" as exported function to JSON::Validator::Joi
- Fix SYNOPSIS for JSON::Validator::Joi
4.02 2020-08-11T13:06:40+0900
- Add recursive_data_protection() #220
Contributor: Jason Cooper
4.01 2020-06-30T17:42:08+0900
- Fix combining "allOf" and "not" #218
4.00 2020-06-08T10:41:55+0900
- JSON::Validator::schema() now holds a JSON::Validator::Schema object
instead of Mojo::JSON::Pointer
- Add schema classes for Draft4, Draft6 and Draft7
- Add "duration" and "uuid" formats #210
- Fix coercing boolean "false" #215
- Fix not matching "null" should also be a "type" error #217
- Deprecated JSON::Validator::joi()
- Deprecated JSON::Validator::singleton()
- Deprecated JSON::Validator::validate_json()
- Deprecated JSON::Validator::version()
- Removed JSON::Validator::generate_definitions_path()
- Removed support for JSON::Validator::bundle({ref_key => ...})
3.25 2020-03-26T07:42:16+0900
- Made "additionalProperties" error message less confusing
- Add support for "items" and "contains" can be defined-but-false #207
Contributor: Karen Etheridge
- Respect "items" when combined with "contains" #207
Contributor: Karen Etheridge
- Require Sereal::Encoder 4.00 to speed up data_checksum()
3.24 2020-03-03T15:46:39+0900
- Optimize checksum generation #202
Contributor: Ere Maijala
- Improved "type":[...] error messages #199
Contributor: Karen Etheridge
- Fix handle validation of true, false schemas in oneOf #201 #203
Contributor: Karen Etheridge
- Fix guessing type:object if "dependencies" is present #206
3.23 2020-02-19T09:37:44+0900
- Using List::Util::uniq() instead of JSON::Validator::Util::uniq() #198
3.22 2020-02-15T08:35:29+0900
- Add support for "dependencies" keyword #192 #197
Contributor: Karen Etheridge
- Add support for anyOf/allOf/oneOf at the same time #196 #197
Contributor: Karen Etheridge
- Allow if/then/else to be in any sort of schema #190 #197
Contributor: Karen Etheridge
3.21 2020-02-14T10:14:14+0900
- Fix data_section() without a class #193
3.20 2020-02-12T09:47:21+0900
- Fix validating draft6 "false" and "true" schemas #190
Contributor: Karen Etheridge
- Add JSON::Validator::Util with E(), data_checksum(), data_section(),
data_type(), is_type(), schema_extract(), json_pointer(),
prefix_errors(), schema_type() and uniq()
- Removed support for JSON_VALIDATOR_REPORT
3.19 2020-02-07T11:10:58+0900
- Add clearer error when more than one oneOf rule matched #184
Contributor: Karen Etheridge
- Improved validation of numeric minimum and maximum values
3.18 2020-01-29T10:18:09+0900
- Fix validating constants that are null or the empty string
Contributor: Karen Etheridge
3.17 2019-12-29T14:21:53+0900
- Fix for deep recursion on Windows #170
- Fix reading unicode specifiation from __DATA__ #179
3.16 2019-10-28T13:59:47+0900
- Add generate_definitions_path() #175 #177
Contributor: Henrik Andersen, Jan Henning Thorsen
3.15 2019-09-27T09:28:32+0900
- Add JSON::Validator::Error->details() #133
- Reversed the checksum and nice name for generated definitions #173
3.14 2019-08-09T23:52:24+0200
- Fix failing tests #169
3.13 2019-08-08T16:27:29+0200
- Fix extend() should not mutate the source objects #167
3.12 2019-08-08T12:02:14+0200
- Fix bug: Joi->extend(...) will also merge "required" #166
- Fix deprecation warning from $joi->validate #165
Contributor: Mattias Päivärinta
- Fix loading Time::Local in the correct module #163
Contributor: Bernhard Graf
3.11 2019-05-07T21:53:16+0700
- Bundle https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.0/schema.json #157
3.10 2019-05-05T14:32:15+0700
- Should not add "/definitions" to bundle, if there are no definitions
- A bit too soon to deprecate bundle({replace => 1}) in 3.09
3.09 2019-05-04T22:28:55+0700
- Prettier definition names from bundle().
- Changed default bundle() definitions location from "x-bundle" to "definitions".
- Deprecated bundle({ref_key => ...})
- Deprecated bundle({replace => ...})
3.08 2019-04-06T15:07:11+0700
- Add support for "default" in object definitions #155
- Add support for coerce("bool,def,num,str") as alternative to hash
- Add support for setting coerce in new()
- Deprecated support for coerce(1)
3.07 2019-04-04T23:43:55+0700
- Fix not leaking file names with bundle()
3.06 2019-02-14T18:24:29+0100
- Fix coercing integers and numbers #147
- Changed recursion guard to not keeping tracking of plain scalars #147
3.05 2019-01-31T08:45:14+0900
- Removed testing Mojo::JSON::MaybeXS, since Mojo::JSON loads Cpanel::JSON::XS
3.04 2019-01-21T09:39:50+0900
- Fix "uri" check, so that it only accept ASCII characters. Note that this
fix might be an undesired change for your application. If so, then update
the "uri" format in your schema to "iri".
- Fix "hostname" format check, so it does not require a valid TLD
- Fix validating draft-07 schema against itself #144
- Add support for more formats in JSON Schema draft-6 and 7: date, idn-email,
idn-hostname, iri, iri-reference, json-pointer, relative-json-pointer, time,
uri-reference and uri-template.
- Add support for more keywords in draft-07
* 6.4.6. Arrays - contains
* 6.5.8. Objects - propertyNames
* 6.6.1. Objects - if
* 6.6.2. Objects - then
* 6.6.3. Objects - else
3.03 2019-01-19T12:11:34+0900
- Add JSON::Validator::Formats with format checks
Note that these functions work by returning a string on error, instead of
true on sucches, which was a breaking change introduced in the 3.00 release.
3.02 2019-01-07T09:52:31+0900
- Trying to fix more failing test reports from the smokers
3.01 2019-01-06T08:16:33+0900
- Fix t/jv-formats.t #140
3.00 2019-01-05T13:13:49+0900
- Add enum() to Joi
- Add support for a list of types passed on to Joi #136
- Add support for file:// scheme in $ref #138
- Fix $ref resolving after fixes applied to Mojo::JSON::Pointer in
Mojolicious 8.11 #139
- Fix cases where input was not coerced
- Breaking change: format callbacks need to return undef on success and a
description on error.
- Changed Joi to always coerce values
- Removed JSON::Validator::OpenAPI
2.19 2018-12-07
- Fix random errors when "type" is a list #126
- Moved JSON::Validator::OpenAPI::Mojolicious to Mojolicious-Plugin-OpenAPI
- Removed JSON::Validator::OpenAPI::Dancer2
2.18 2018-11-15
- Add EXPERIMENTAL support for data:// without a package
2.17 2018-11-14
- Add basic support for OpenAPI v3
2.16 2018-11-14
- Improved openapi "date" format validation #123
Contributor: Jason Cooper
2.15 2018-11-07
- Did not need to bump Mojolicious version in 2.15 #122
Contributor: Dagfinn Ilmari Mannsåker
2.14 2018-10-26
- Fix guessing schema type from "required" key #118
- Fix appending parameters for Mojolicious 8.00 #119 #120
- Improved error return values from allOf, anyOf and oneOf validation
- Will not overwrite OpenAPI "/info/version" from "version_from_class"
2.13 2018-10-17
- Compatible with weak attrs in Mojolicious 8.03
2.12 2018-10-03
- Improved error message when $ref is pointing to a non-existing file
2.11 2018-09-30
- Skipping load-from-app.t on "Gateway Timeout" as well
2.10 2018-09-26
- Fix handling of directory name with RFC 3986 reserved chars
Contributor: Ed J
2.09 2018-09-26
- Skip "remote ref" tests when running through cpantesters
2.08 2018-06-03
- Fix validating oneOf correctly #103
- Fix validating "id" property #111
- Add support for $id keyword in draft-07 #114
- Bundle JSON Schema draft-06 and draft-07
2.07 2018-04-18
- Fix joi->object->strict()
Contributor: Pierre-Aymeric Masse
2.06 2018-04-09
- Fix normalising file names on windows #102
- Prevent "Use of uninitialized value $pointer in length..." for older Perls #104
- Removed warning about coercion now, since it works well
2.05 2018-03-11
- Fix hash randomization problem fot t/get.t #101
2.04 2018-03-10
- Add JSON::Validator::JOI and joi() #63
- Add support for get(|"x", undef, "y"])
- Will catch if more than one parameter has "in":"body" #97
- Fix file-path with ".." gets false negative for same-ref check #99
2.03 2018-02-15
- Will not leak file system information to bundled schema
2.02 2018-01-30
- Will let the user know if YAML::XS 0.67 (or later) need to be installed
2.01 2018-01-26
- Fix bundle method not spotting "local" fqn when schema from URL
- Forgot to remove deprecated JSON_VALIDATOR_CACHE_DIR in 2.00
2.00 2018-01-19
- Fix validating against any enum value #22
- Require YAML::XS 0.67 for proper boolean handling
- Removed support for parsing YAML with YAML::Syck
- Removed deprecated method load_and_validate_spec()
1.08 2017-12-24
- Fix setting default value from $ref jhthorsen/mojolicious-plugin-openapi#53
- Skip load-from-app.t if "Service Unavailable"
1.07 2017-11-27
- Can load schame from internal app #85
1.06 2017-11-19
- Add JSON::Validator::get()
- Add JSON::Validator::bundle()
- A $ref is tied hashes, represented by JSON::Validator::Ref
1.05 2017-10-22
- Fix validating headers regardless of case #77
Contributor: Aleksandr Orlenko
- Improved boolean handling #76
Contributor: Aleksandr Orlenko
- Improved URI validation, fixes #74
- Resolving "$ref" on the fly #65 #75 #79
1.04 2017-10-05
- Avoid autovivification of "patternProperties" in the input schema #47
1.03 2017-09-25
- Fix "uri" format validation, closes #70
1.02 2017-09-01
- Fix validating "type" and "enum" #69
1.01 2017-08-19
- Add support for fetching specification from local application
1.00 2017-06-20
- Removed EXPERIMENTAL from JSON::Validator::OpenAPI (1.00)
- Coerce integer numbers into booleans #67
Contributor: @fabzzap
0.99 2017-06-12
- Hopefully fixed some Windows issues #60
0.98 2017-05-21
- Add support for "const" #62
Contributor: Kevin Goess
0.97 2017-03-21
- Require a newer version of Test::More to build
0.96 2017-03-06
- Fix JSON::Validator::load_and_validate_schema()
- Add handling of header/formData/query as array #38
- Allow alternative date-time separator #49
- Improved recursion tracking #52
- More tests in t/acceptance.t are ok #52
- Avoid loading the same file multiple times #54
- Swagger2 is deprecated
0.95 2017-03-02
- Add support for format "password"
- Add load_and_validate_schema() to JSON::Validator #51
- Started deprecating load_and_validate_spec()
0.94 2017-02-13
- Fix t/issue-27-yaml-syck-false.t
- Removed Carp::Always #47
0.93 2017-02-13
- Fix coercing YAML booleans in input specification jhthorsen/mojolicious-plugin-openapi#24
- Replace JSON_VALIDATOR_CACHE_DIR with JSON_VALIDATOR_CACHE_PATH
- Remove deprecated cache_dir attribute
0.92 2017-01-18
- Fix infinite recursion when resolving self referencing data structures
0.91 2017-01-10
- Mojo::Util::slurp is DEPRECATED in favor of Mojo::File::slurp
0.90 2016-12-11
- Add support for validating Dancer2 requset/responses #34
- Fix invalidating integer/number path part with letters #37
0.89 2016-11-05
- Fix multipleOf:0.01, closes #35
0.88 2016-11-04
- Fix number coercion #32
Contributor: @melhesedek
- Add JSON::Validator::OpenAPI->load_and_validate_spec()
0.87 2016-10-20
- Fix validating data when boolean.pm is loaded
0.86 2016-10-06
- Documented bundled resources
0.85 2016-09-26
- Fix handling of collectionFormat where no input is defined
0.84 2016-08-19
- Removed support for passing $json_path to validate()
- Fix guessing type of objects that has TO_JSON()
0.83 2016-08-16
- Fix handling of true/false in schema, when loaded with YAML::Syck #27
- Add EXPERIMENTAL support for passing $json_path to validate()
0.82 2016-08-09
- Fix finding all $ref occurances jhthorsen/swagger2#95
0.81 2016-08-08
- Add support for multiple cache dir search paths
- Deperecated cache_dir()
- Fix recurring requests with same path part jhthorsen/swagger2#93
- Fix "Use of uninitialized value $schema_type..." warnings
0.80 2016-08-03
- Fix parsing recursive schema
0.79 2016-07-28
- Reverted improved allOf, anyOf and oneOf error messages
0.78 2016-07-28
- Fix recursive dependencies #23
- Add EXPERIMENTAL resolver attribute
- Improved allOf, anyOf and oneOf error messages
0.77 2016-07-26
- Avoid duplicate error messages with enum #22
- Fix "false" must be false and not true in OpenAPI
0.76 2016-07-25
- Will write default values into Mojolicious::Controller
0.75 2016-07-02
- Fix uploads must not be slurped
- Fix reporting error on missing response status definition
- Add warnings on invalid (Perl) regexes
0.74 2016-06-22
- Fix length($data) need be defined in 5.10
0.73 2016-06-22
- Add http://git.io/vcKD4 error schema to cache
- Add JSON schema for JSONPatch files
- Updated Swagger2 spec to https://github.com/OAI/OpenAPI-Specification/blob/19fed9f0f812ccebe0fc45313fea75bb6656de1c/schemas/v2.0/schema.json
0.72 2016-06-10
- Fix default cache_dir() path
- JSON::Validator is no longer EXPERIMENTAL
- Move Swagger2::SchemaValidator into JSON::Validator::OpenAPI
0.71 2016-06-07
- Fix setting schema() inside validate()
0.70 2016-05-31
- Fix allowing "id" as property name in objects
0.69 2016-05-26
- Fix failing anyOf logic in t/swagger-validate-response-object.t
0.68 2016-05-25
- Remove _merge_error to clarify anyOf errors #15
0.67 2016-04-11
- Add JSON::Validator::Error class
0.66 2016-02-09
- Fix validating recursive datastructures
0.65 2016-01-07
- Fix t/swagger-validate-response-object.t require Swagger2 0.66 #14
0.64 2015-12-18
- Fix treating JSON::PP::Boolean objects as boolean #13
Contributor: Krasimir Berov
- Allow hash reference as arguments to coerce #13
Contributor: Krasimir Berov
0.63 2015-11-28
- Fix skip test in t/booleans.t
0.62 2015-11-27
- Remove support for YAML.pm #jhthorsen/swagger2#50
- Remove support for YAML::Tiny #jhthorsen/swagger2#50
0.61 2015-11-11
- Fix use of TO_JSON() on objects inside arrays #12
0.60 2015-11-09
- Can use TO_JSON() when validating perl objects
0.59 2015-10-14
- Move "collectionFormat" support to Swagger2
0.58 2015-10-13
- Fix string "0" is not detected as boolean
0.57 2015-10-11
- Trust guesswork if input data is undefined
0.56 2015-09-30
- Can read YAML::XS booleans automatically #8
- Change coerce() into a method. #8
- Remove EXPERIMENTAL coerce attribute #8
- Remove EXPERIMENTAL JSON_VALIDATOR_COERCE_VALUES and SWAGGER_COERCE_VALUES #8
0.55 2015-09-29
- Fix "required" cannot be a boolean on properties
- Improved documentation of error object
- Change anyOf/allOf/oneOf error message
0.54 2015-09-27
- Add support for $ref to relative path #3 #4 #5
- Removed Swagger specific type "file"
- Removed Swagger specific formats: "byte", "date", "double", "float", "int32" and "int64".
0.53 2015-09-13
- Fix properties, patternProperties, additionalProperties interaction - patternProperty invalidates property
- Fix validation for a keyword and instance SHOULD succeed when keywords does not match primitive type
- Fix allOf with base schema - mismatch base schema
- Fix checking for a boolean "required"
0.52 2015-09-05
- Add guessing of schema type, based on other attributes
- More strict on what is validated as "boolean"
- Fix additionalItems are allowed by default
- Fix additionalProperties allows a schema which should validate
- Fix validating "enum"
- Fix validating "array" against "additionalItems"
- Fix bugs after running
https://github.com/Relequestual/Test-JSON-Schema-Acceptance to validate
0.51 2015-08-24
- Fix "$ref" pointing to a file on disk #1
0.50 2015-08-23
- Fix missing namespace when registering new document
- Made cache_dir() public
- Bundled spec for json-schema and swagger
0.49 2015-08-23
- Fix loading schema from files
0.48 2015-08-22
- Merged core functionality from Swagger2 and Swagger2::SchemaValidator
into this module, JSON::Validator
See https://metacpan.org/source/JHTHORSEN/Swagger2-0.47/Changes for
previous Changes (<=0.47)
- Fix coercing collectionFormat strings into integers and numbers
- Add support for reading schemas from __DATA__ section
JSON-Validator-5.18/MANIFEST 0000644 0000765 0000024 00000017006 15211411470 015163 0 ustar jhthorsen staff Changes
CONTRIBUTING.md
lib/JSON/Validator.pm
lib/JSON/Validator/cache/089e74a6d17f64af17a9efd6d0fa0de6
lib/JSON/Validator/cache/10a5eeb37fcd5d829449028f7ceb0774
lib/JSON/Validator/cache/33912dbbde6e1d936140f1c82b283d01
lib/JSON/Validator/cache/36d1bd12eeed51e86c8695bd8876a9df
lib/JSON/Validator/cache/3be3f46eb248daf48925640f8ef057e8
lib/JSON/Validator/cache/3d35aac549d951f4cf9182ff47bff0b4
lib/JSON/Validator/cache/4550dd8afbfee9e71377b45f5fea42ce
lib/JSON/Validator/cache/49c95b866e40f788892a7fb3c816b0e8
lib/JSON/Validator/cache/4a31fe43be9e23ca9eb8d9e9faba8892
lib/JSON/Validator/cache/546acf85ddc442761c18517490215b90
lib/JSON/Validator/cache/630949337805585c8e52deea27d11419
lib/JSON/Validator/cache/7fe97ed1a4c3fac607dd276b2b298275
lib/JSON/Validator/cache/a0f5b4b4e75ea17fc09e88ec0343d148
lib/JSON/Validator/cache/a516498b60c53096b2ce2cd83ebe0abc
lib/JSON/Validator/cache/c6f188eb288cf986f23db49297b25e83
lib/JSON/Validator/cache/d18065ce8fb1f748e766b2737bae5200
lib/JSON/Validator/cache/d8cf7ae7a0fd14accadf5d18bc84d14f
lib/JSON/Validator/cache/ea34d47d4e060a1c3b12d2287aff89a7
lib/JSON/Validator/cache/eaa832720f36cff0abc20c05236a9cd9
lib/JSON/Validator/Error.pm
lib/JSON/Validator/Formats.pm
lib/JSON/Validator/Joi.pm
lib/JSON/Validator/Schema.pm
lib/JSON/Validator/Schema/Draft201909.pm
lib/JSON/Validator/Schema/Draft4.pm
lib/JSON/Validator/Schema/Draft6.pm
lib/JSON/Validator/Schema/Draft7.pm
lib/JSON/Validator/Schema/OpenAPIv2.pm
lib/JSON/Validator/Schema/OpenAPIv3.pm
lib/JSON/Validator/Store.pm
lib/JSON/Validator/URI.pm
lib/JSON/Validator/Util.pm
Makefile.PL
MANIFEST This list of files
t/00-project.t
t/benchmark.t
t/bundle.t
t/coerce-default.t
t/coerce.t
t/deep-mixed-ref.t
t/definitions/age.json
't/definitions/space age.json'
t/definitions/unit.json
t/definitions/weight.json
t/draft2019-09-acceptance.t
t/draft2019-09.t
t/draft4-acceptance.t
t/draft4.t
t/draft6-acceptance.t
t/draft6.t
t/draft7-acceptance.t
t/draft7.t
t/get.t
t/Helper.pm
t/id-keyword-draft4.t
t/id-keyword-draft7.t
t/invalid-ref.t
t/issue-103-one-of.t
t/issue-158-draf7-coerce-defaults.t
t/issue-22-duplicate-error-messages.t
t/issue-272-recursive-data-protection.t
t/issue-42-cache-control.t
t/issue-59-oneof-blessed-booleans.t
t/issue-71-additionalproperties.t
t/joi.t
t/jv-allof-and-not.t
t/jv-allof.t
t/jv-anyof.t
t/jv-array.t
t/jv-basic.t
t/jv-boolean.t
t/jv-const.t
t/jv-enum.t
t/jv-formats.t
t/jv-if-then-else.t
t/jv-integer.t
t/jv-not.t
t/jv-number.t
t/jv-object.t
t/jv-oneof.t
t/jv-required.t
t/jv-string.t
t/load-data.t
t/load-file.t
t/load-from-app.t
t/load-http.t
t/load-json.t
t/load-yaml-pp.t
t/load-yaml.t
t/more-bundle.t
t/newline-warnings.t
t/openapiv2-basic.t
t/openapiv2-bundle.t
t/openapiv2-collection-format.t
t/openapiv2-default-values.t
t/openapiv2-discriminator.t
t/openapiv2-file.t
t/openapiv2-headers.t
t/openapiv2-readonly.t
t/openapiv2-routes.t
t/openapiv3-basic.t
t/openapiv3-bundled-spec.t
t/openapiv3-coerce-array.t
t/openapiv3-content-types.t
t/openapiv3-default-values.t
t/openapiv3-discriminator.t
t/openapiv3-file.t
t/openapiv3-nullable.t
t/openapiv3-readonly-writeonly.t
t/openapiv3-style-explode.t
t/predictable-errors.t
t/random-errors.t
t/recursive_data_protection.t
t/relative-ref.t
t/remotes/folder/folderInteger.json
t/remotes/integer.json
t/remotes/subSchemas.json
t/spec/bundle-no-leaking-filename.json
t/spec/bundlecheck.json
t/spec/missing-ref.json
t/spec/more-bundle.yaml
t/spec/more-bundle2.yaml
t/spec/paths.yaml
t/spec/person.json
t/spec/remotes/baseUriChange/folderInteger.json
t/spec/remotes/baseUriChangeFolder/folderInteger.json
t/spec/remotes/baseUriChangeFolderInSubschema/folderInteger.json
t/spec/remotes/different-id-ref-string.json
t/spec/remotes/draft-next/baseUriChange/folderInteger.json
t/spec/remotes/draft-next/baseUriChangeFolder/folderInteger.json
t/spec/remotes/draft-next/baseUriChangeFolderInSubschema/folderInteger.json
t/spec/remotes/draft-next/extendible-dynamic-ref.json
t/spec/remotes/draft-next/format-assertion-false.json
t/spec/remotes/draft-next/format-assertion-true.json
t/spec/remotes/draft-next/integer.json
t/spec/remotes/draft-next/locationIndependentIdentifier.json
t/spec/remotes/draft-next/metaschema-no-validation.json
t/spec/remotes/draft-next/name-defs.json
t/spec/remotes/draft-next/nested/foo-ref-string.json
t/spec/remotes/draft-next/nested/string.json
t/spec/remotes/draft-next/ref-and-defs.json
t/spec/remotes/draft-next/subSchemas-defs.json
t/spec/remotes/draft-next/subSchemas.json
t/spec/remotes/draft-next/tree.json
t/spec/remotes/draft2019-09/baseUriChange/folderInteger.json
t/spec/remotes/draft2019-09/baseUriChangeFolder/folderInteger.json
t/spec/remotes/draft2019-09/baseUriChangeFolderInSubschema/folderInteger.json
t/spec/remotes/draft2019-09/dependentRequired.json
t/spec/remotes/draft2019-09/extendible-dynamic-ref.json
t/spec/remotes/draft2019-09/ignore-prefixItems.json
t/spec/remotes/draft2019-09/integer.json
t/spec/remotes/draft2019-09/locationIndependentIdentifier.json
t/spec/remotes/draft2019-09/metaschema-no-validation.json
t/spec/remotes/draft2019-09/name-defs.json
t/spec/remotes/draft2019-09/nested/foo-ref-string.json
t/spec/remotes/draft2019-09/nested/string.json
t/spec/remotes/draft2019-09/ref-and-defs.json
t/spec/remotes/draft2019-09/subSchemas-defs.json
t/spec/remotes/draft2019-09/subSchemas.json
t/spec/remotes/draft2019-09/tree.json
t/spec/remotes/draft2020-12/baseUriChange/folderInteger.json
t/spec/remotes/draft2020-12/baseUriChangeFolder/folderInteger.json
t/spec/remotes/draft2020-12/baseUriChangeFolderInSubschema/folderInteger.json
t/spec/remotes/draft2020-12/extendible-dynamic-ref.json
t/spec/remotes/draft2020-12/format-assertion-false.json
t/spec/remotes/draft2020-12/format-assertion-true.json
t/spec/remotes/draft2020-12/integer.json
t/spec/remotes/draft2020-12/locationIndependentIdentifier.json
t/spec/remotes/draft2020-12/metaschema-no-validation.json
t/spec/remotes/draft2020-12/name-defs.json
t/spec/remotes/draft2020-12/nested/foo-ref-string.json
t/spec/remotes/draft2020-12/nested/string.json
t/spec/remotes/draft2020-12/prefixItems.json
t/spec/remotes/draft2020-12/ref-and-defs.json
t/spec/remotes/draft2020-12/subSchemas-defs.json
t/spec/remotes/draft2020-12/subSchemas.json
t/spec/remotes/draft2020-12/tree.json
t/spec/remotes/draft7/ignore-dependentRequired.json
t/spec/remotes/extendible-dynamic-ref.json
t/spec/remotes/folder/folderInteger.json
t/spec/remotes/integer.json
t/spec/remotes/locationIndependentIdentifier.json
t/spec/remotes/locationIndependentIdentifierDraft4.json
t/spec/remotes/locationIndependentIdentifierPre2019.json
t/spec/remotes/name-defs.json
t/spec/remotes/name.json
t/spec/remotes/nested-absolute-ref-to-string.json
t/spec/remotes/nested/foo-ref-string.json
t/spec/remotes/nested/string.json
t/spec/remotes/ref-and-definitions.json
t/spec/remotes/ref-and-defs.json
t/spec/remotes/subSchemas-defs.json
t/spec/remotes/subSchemas.json
t/spec/remotes/tree.json
t/spec/remotes/urn-ref-string.json
't/spec/space bundle.json'
t/spec/test-definitions-key.json
t/spec/v2-bundle.yaml
t/spec/v2-petstore.json
t/spec/v3-default-response-extra.yaml
t/spec/v3-petstore.json
t/spec/with-deep-mixed-ref.json
t/spec/with-relative-ref.json
t/spec/with-unicode-multibyte.json
t/spec/with-unicode-multibyte.yml
t/stack/Some.pm
t/stack/Some/Module.pm
t/test/array.pm
t/test/number.pm
t/test/object.pm
t/to-json.t
t/unicode-multibyte.t
t/uri.t
t/util-checksum-yaml-xs.t
t/util.t
t/validate-draft07.t
t/validate-id.t
t/validate-recursive.t
t/validate-schema.t
META.yml Module YAML meta-data (added by MakeMaker)
META.json Module JSON meta-data (added by MakeMaker)
JSON-Validator-5.18/t/ 0000755 0000765 0000024 00000000000 15211411467 014277 5 ustar jhthorsen staff JSON-Validator-5.18/t/openapiv3-readonly-writeonly.t 0000644 0000765 0000024 00000003727 15210277065 022251 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my ($body, @errors);
$body = sub { +{exists => 1, value => {birth => '1983-02-24'}} };
@errors = $schema->validate_request([post => '/user'], {body => $body});
is "@errors", '', 'required is ignored on validate_request';
$body = sub { +{exists => 1, value => {age => 42, birth => '1983-02-24'}} };
@errors = $schema->validate_request([post => '/user'], {body => $body});
is "@errors", '/body/age: Read-only.', 'age is read-only for request';
$body = sub { +{exists => 1, value => {}} };
@errors = $schema->validate_response([get => '/user'], {body => $body});
is "@errors", '/body/age: Missing property.', 'age is required in response';
$body = sub { +{exists => 1, value => {age => 42}} };
@errors = $schema->validate_response([get => '/user'], {body => $body});
is "@errors", '', 'age is present in response';
$body = sub { +{exists => 1, value => {age => 42, birth => '1983-02-24'}} };
@errors = $schema->validate_response([get => '/user'], {body => $body});
is "@errors", '/body/birth: Write-only.', 'birth is write-only in response';
done_testing;
__DATA__
@@ spec.json
{
"openapi": "3.0.0",
"info": { "title": "Read/write-only", "version": "" },
"paths": {
"/user": {
"get": {
"responses": {
"200": {
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }
}
}
},
"post": {
"requestBody": {
"required": true,
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }
}
}
}
},
"components": {
"schemas": {
"User": {
"type": "object",
"required": ["age", "birth"],
"properties": {
"age": {"type": "integer", "readOnly": true},
"birth": {"type": "string", "writeOnly": true}
}
}
}
}
}
JSON-Validator-5.18/t/coerce-default.t 0000644 0000765 0000024 00000002164 15210277065 017354 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::JSON qw(false true);
use Test::More;
my $jv = JSON::Validator->new(coerce => 'defaults');
is_deeply($jv->coerce, {defaults => 1}, 'coerce defaults');
$jv->coerce('def');
is_deeply($jv->coerce, {defaults => 1}, 'coerce def');
$jv->schema({
'$schema' => 'http://json-schema.org/draft-04/schema#',
type => 'object',
definitions => {subscribed_to => {type => 'array', default => []}},
properties =>
{tos => {type => 'boolean', default => false}, subscribed_to => {'$ref' => '#/definitions/subscribed_to'}},
});
my $data = {};
my @errors = $jv->validate($data);
is_deeply \@errors, [], 'defaults pass validation';
is_deeply $data, {tos => false, subscribed_to => []}, 'data was mutated';
$data->{tos} = true;
@errors = $jv->validate($data);
is_deeply $data, {tos => true, subscribed_to => []}, 'only subscribed_to was mutated';
$jv->schema({type => 'object', properties => {age => {type => 'number', default => 'invalid'}}});
@errors = $jv->validate({});
is $errors[0]->message, 'Expected number - got string.', 'default values must be valid';
done_testing;
JSON-Validator-5.18/t/jv-required.t 0000644 0000765 0000024 00000001226 15210277065 016725 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema0 = {type => 'object', properties => {mynumber => {type => 'string', required => 1}}};
my $schema1 = {type => 'object', properties => {mynumber => {type => 'string'}}, required => ['mynumber']};
my $schema2 = {type => 'object', properties => {mynumber => {type => 'string'}}};
my $data1 = {mynumber => 'yay'};
my $data2 = {mynumbre => 'err'};
validate_ok $data1, $schema1;
validate_ok $data2, $schema0; # Cannot have required on properties
validate_ok $data2, $schema1, E('/mynumber', 'Missing property.');
validate_ok $data1, $schema2;
validate_ok $data2, $schema2;
done_testing;
JSON-Validator-5.18/t/util-checksum-yaml-xs.t 0000644 0000765 0000024 00000002167 15210277065 020642 0 ustar jhthorsen staff BEGIN {
unshift @INC, sub {
my $file = $_[1];
die "Skipping $file in this test" if $file =~ m!Sereal\W+Encoder\.pm$!;
};
}
use Mojo::Util 'md5_sum';
use JSON::Validator;
use JSON::Validator::Util qw(data_checksum);
use Test::More;
my $d_hash = {foo => {}, bar => {}};
my $d_hash2 = {bar => {}, foo => {}};
my $d_undef = {foo => undef};
my $d_obj = {foo => JSON::Validator::Error->new};
my $d_array = ['foo', 'bar'];
my $d_array2 = ['bar', 'foo'];
ok !$INC{'Sereal/Encoder.pm'}, 'Sereal::Encoder was not loaded';
isnt data_checksum($d_array), data_checksum($d_array2), 'data_checksum array';
is data_checksum($d_hash), data_checksum($d_hash2), 'data_checksum hash field order';
isnt data_checksum($d_hash), data_checksum($d_undef), 'data_checksum hash not undef';
isnt data_checksum($d_hash), data_checksum($d_obj), 'data_checksum hash not object';
isnt data_checksum($d_obj), data_checksum($d_undef), 'data_checksum object not undef';
isnt data_checksum(3.14), md5_sum(3.15), 'data_checksum numeric';
is data_checksum(3.14), data_checksum('3.14'), 'data_checksum numeric like string';
done_testing;
JSON-Validator-5.18/t/jv-not.t 0000644 0000765 0000024 00000000251 15210277065 015702 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema = {not => {type => 'string'}};
validate_ok 12, $schema;
validate_ok 'str', $schema, E('/', 'Should not match.');
done_testing;
JSON-Validator-5.18/t/jv-basic.t 0000644 0000765 0000024 00000001123 15210277065 016162 0 ustar jhthorsen staff use lib '.';
use t::Helper;
sub j { Mojo::JSON::decode_json(Mojo::JSON::encode_json($_[0])); }
validate_ok j($_), {type => 'any'} for undef, [], {}, 123, 'foo';
validate_ok j(undef), {type => 'null'};
validate_ok j(1), {type => 'null'}, E('/', 'Expected null - got number.');
validate_ok($_, {}) foreach (true, false, 1, 1.2, 'a string', {a => 'b'}, [1, 2, 3]);
validate_ok($_, true) foreach (true, false, 1, 1.2, 'a string', {a => 'b'}, [1, 2, 3]);
validate_ok($_, false, E('/', 'Should not match.')) foreach (true, false, 1, 1.2, 'a string', {a => 'b'}, [1, 2, 3]);
done_testing;
JSON-Validator-5.18/t/load-http.t 0000644 0000765 0000024 00000001076 15210277065 016367 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
plan skip_all => 'TEST_ONLINE=1' unless $ENV{TEST_ONLINE};
my $jv = JSON::Validator->new;
$jv->schema('http://swagger.io/v2/schema.json');
isa_ok $jv->schema, 'JSON::Validator::Schema';
like $jv->schema->get('/title'), qr{swagger}i, 'got swagger spec';
ok $jv->schema->get('/patternProperties/^x-/description'), 'resolved vendorExtension $ref';
is_deeply [sort keys %{$jv->store->schemas}],
['http://json-schema.org/draft-04/schema', 'http://swagger.io/v2/schema.json'], 'schemas in store';
done_testing;
JSON-Validator-5.18/t/id-keyword-draft4.t 0000644 0000765 0000024 00000005506 15210277065 017735 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::JSON 'encode_json';
use Test::Mojo;
use Test::More;
my ($base_url, $jv, $t, @e);
use Mojolicious::Lite;
get '/invalid-fragment' => [format => ['json']] => 'invalid-fragment';
get '/invalid-relative' => [format => ['json']] => 'invalid-relative';
get '/relative-to-the-root' => [format => ['json']] => 'relative-to-the-root';
$t = Test::Mojo->new;
$jv = JSON::Validator->new(ua => $t->ua);
$t->get_ok('/relative-to-the-root.json')->status_is(200);
$base_url = $t->tx->req->url->to_abs->path('/');
like $base_url, qr{^http}, 'got base_url to web server';
eval { $jv->load_and_validate_schema("${base_url}relative-to-the-root.json") };
ok !$@, "${base_url}relative-to-the-root.json" or diag $@;
isa_ok $jv->schema, 'JSON::Validator::Schema::Draft4';
my $schema = $jv->schema;
is $schema->moniker, 'draft04', 'moniker';
is $schema->specification, 'http://json-schema.org/draft-04/schema#', 'specification';
is $schema->get('/id'), 'http://example.com/relative-to-the-root.json', 'get /id';
is $schema->get('/definitions/B/id'), 'b.json', 'id /definitions/B/id';
is $schema->get('/definitions/B/definitions/X/id'), '#bx', 'id /definitions/B/definitions/X/id';
is $schema->get('/definitions/B/definitions/Y/id'), 't/inner.json', 'id /definitions/B/definitions/Y/id';
is $schema->get('/definitions/C/definitions/X/id'), 'urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f',
'id /definitions/C/definitions/X/id';
is $schema->get('/definitions/C/definitions/Y/id'), '#cy', 'id /definitions/C/definitions/Y/id';
my $r1 = $schema->get('/definitions/R1');
is encode_json($r1), '{"id":"#bx"}', 'R1 encode_json';
eval { $jv->load_and_validate_schema("${base_url}invalid-fragment.json") };
like $@, qr{Fragment not allowed}, 'Root id cannot have a fragment' or diag $@;
eval { $jv->load_and_validate_schema("${base_url}invalid-relative.json") };
like $@, qr{Relative URL not allowed}, 'Root id cannot be relative' or diag $@;
done_testing;
__DATA__
@@ invalid-fragment.json.ep
{"id": "http://example.com/invalid-fragment.json#cannot_be_here"}
@@ invalid-relative.json.ep
{"id": "whatever"}
@@ relative-to-the-root.json.ep
{
"id": "http://example.com/relative-to-the-root.json",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"A": { "id": "#a" },
"B": {
"id": "b.json",
"definitions": {
"X": { "id": "#bx" },
"Y": { "id": "t/inner.json" }
}
},
"C": {
"id": "c.json",
"definitions": {
"X": { "id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f" },
"Y": { "id": "#cy" }
}
},
"R1": { "$ref": "b.json#bx" },
"R2": { "$ref": "#a" },
"R3": { "$ref": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f" }
}
}
JSON-Validator-5.18/t/openapiv2-discriminator.t 0000644 0000765 0000024 00000005764 15210277065 021253 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my ($body, @errors);
my %cat = (name => 'kit-e-cat', petType => 'Cat', huntingSkill => 'adventurous');
my %dog = (name => 'dog-e-dog', petType => 'Dog', packSize => 4);
$body = sub { {exists => 1, value => {%cat, petType => 'Dog'}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body/packSize: /allOf/1 Missing property.', 'invalid dog';
$body = sub { {exists => 1, value => {%cat}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '', 'valid cat';
$body = sub { {exists => 1, value => {%dog, petType => 'Cat'}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body/huntingSkill: /allOf/1 Missing property.', 'invalid cat';
$body = sub { {exists => 1, value => {%dog}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '', 'valid dog';
$body = sub { {exists => 1, value => {%dog, petType => ''}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body: Discriminator petType has no value.', 'discriminator is required';
$body = sub { {exists => 1, value => {%dog, petType => 'Hamster'}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body: No definition for discriminator Hamster.', 'invalid discriminator';
done_testing;
__DATA__
@@ spec.json
{
"swagger": "2.0",
"info": {"version": "", "title": "Test discriminator"},
"basePath": "/api",
"paths": {
"/pets": {
"post": {
"parameters": [
{"in": "body", "name": "body", "schema": {"$ref": "#/definitions/Pet"}}
],
"responses": {
"200": {
"description": "pet response",
"schema": {"type": "object"}
}
}
}
}
},
"definitions": {
"Pet": {
"type": "object",
"discriminator": "petType",
"required": ["name", "petType"],
"properties": {
"name": {"type": "string"},
"petType": {"type": "string"}
}
},
"Cat": {
"description": "A representation of a cat",
"allOf": [
{"$ref": "#/definitions/Pet"},
{
"type": "object",
"required": ["huntingSkill"],
"properties": {"huntingSkill": {"type": "string", "description": "The measured skill for hunting", "default": "lazy", "enum": ["clueless", "lazy", "adventurous", "aggressive"]}
}
}
]
},
"Dog": {
"description": "A representation of a dog",
"allOf": [
{"$ref": "#/definitions/Pet"},
{
"type": "object",
"required": ["packSize"],
"properties": {
"packSize": {"type": "integer", "format": "int32", "description": "the size of the pack the dog is from", "default": 0, "minimum": 0}
}
}
]
}
}
}
JSON-Validator-5.18/t/validate-draft07.t 0000644 0000765 0000024 00000000672 15210277065 017532 0 ustar jhthorsen staff use Mojo::Base -strict;
use Mojo::File 'path';
use Mojo::JSON 'decode_json';
use Test::More;
use JSON::Validator;
my $draft07 = path(qw(lib JSON Validator cache 4a31fe43be9e23ca9eb8d9e9faba8892));
plan skip_all => "Cannot open $draft07" unless -r $draft07;
my $schema = decode_json($draft07->slurp);
my @errors = JSON::Validator->new->validate($schema, $schema);
ok !@errors, "validated draft07" or map { diag $_ } @errors;
done_testing;
JSON-Validator-5.18/t/issue-71-additionalproperties.t 0000644 0000765 0000024 00000000424 15210277065 022267 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema
= {required => ['link'], type => 'object', additionalProperties => false, properties => {link => {format => 'uri'}}};
validate_ok {haha => 'hehe', link => 'http://a'}, $schema, E('/', 'Properties not allowed: haha.');
done_testing;
JSON-Validator-5.18/t/load-from-app.t 0000644 0000765 0000024 00000002154 15210277065 017127 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojolicious;
use Test::More;
my $jv = JSON::Validator->new;
$jv->ua->server->app(Mojolicious->new);
$jv->ua->server->app->log(Mojo::Log->new->level('fatal'));
$jv->ua->server->app->routes->get(
'/spec' => sub {
my $c = shift;
die 'not cached' if $c->stash('from_cache');
$c->render(json => {'$ref' => 'http://swagger.io/v2/schema.json'});
}
);
# Some CPAN testers says "Service Unavailable"
eval { $jv->schema('/spec') };
plan skip_all => $@ if $@ =~ /\bGET\b/i;
is $jv->store->ua, $jv->ua, 'shared ua';
is $@, '', 'loaded schema from app';
is $jv->get('/properties/swagger/enum/0'), '2.0', 'loaded schema structure';
is_deeply [sort keys %{$jv->store->schemas}],
['/spec', 'http://json-schema.org/draft-04/schema', 'http://swagger.io/v2/schema.json'], 'schemas in store';
$jv->ua->server->app->defaults(from_cache => 1);
ok $jv->schema('/spec'), 'loaded from cache';
$jv->store->schemas({});
eval { $jv->schema('/spec') };
like $@, qr{Internal Server Error}, 'cache cleared';
done_testing;
JSON-Validator-5.18/t/draft7.t 0000644 0000765 0000024 00000002512 15210277065 015656 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use JSON::Validator::Schema::Draft7;
t::Helper->schema(JSON::Validator::Schema::Draft7->new);
t::Helper->test(number => qw(basic maximum minimum));
t::Helper->test(array => qw(basic items additional_items contains min_max));
t::Helper->test(array => qw(unique unevaluated_items));
t::Helper->test(object => qw(basic properties));
t::Helper->test(object => qw(additional_properties pattern_properties min_max names));
subtest 'exclusiveMaximum' => sub {
schema_validate_ok 2.4, {exclusiveMaximum => 2.4}, E('/', '2.4 >= maximum(2.4)');
schema_validate_ok 0, {exclusiveMaximum => 0}, E('/', '0 >= maximum(0)');
};
subtest 'exclusiveMinimum' => sub {
schema_validate_ok 4.2, {exclusiveMinimum => 4.2}, E('/', '4.2 <= minimum(4.2)');
schema_validate_ok 0, {exclusiveMinimum => 0}, E('/', '0 <= minimum(0)');
};
subtest 'bundle' => sub {
my $bundle = JSON::Validator::Schema::Draft7->new('data://main/spec.json')->bundle;
is $bundle->data->{properties}{name}{'$ref'}, '#/definitions/defs_json-name', 'bundle ref';
is $bundle->data->{definitions}{'defs_json-name'}{type}, 'string', 'bundled spec under $defs';
};
done_testing;
__DATA__
@@ spec.json
{"type":"object","properties":{"name":{"$ref":"data://main/defs.json#/name"}}}
@@ defs.json
{"name":{"type":"string"}}
JSON-Validator-5.18/t/unicode-multibyte.t 0000644 0000765 0000024 00000002122 15210277065 020126 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use Mojo::File qw(path);
use Mojo::Util qw(encode);
use Test::More;
my $json_file = path(__FILE__)->dirname->child('spec')->child('with-unicode-multibyte.json');
my $yaml_file = path(__FILE__)->dirname->child('spec')->child('with-unicode-multibyte.yml');
my $perl_utf8_str = "foo\x{266b}bar";
my $encoded_bytes = encode('UTF-8', $perl_utf8_str);
my $with_replacement_char = "replacement\x{fffd}char";
my $invalid_utf8_1 = "replacement\x{d800}char";
my $invalid_utf8_2 = "replacement\x{d8f0}char";
validate_ok {foo => $perl_utf8_str}, $json_file;
validate_ok {foo => $encoded_bytes}, $json_file, E('/foo', "Not in enum list: $perl_utf8_str.");
validate_ok {foo => $perl_utf8_str}, $yaml_file;
validate_ok {foo => $encoded_bytes}, $yaml_file, E('/foo', "Not in enum list: $perl_utf8_str.");
validate_ok {bar => $with_replacement_char}, $json_file;
validate_ok {bar => $invalid_utf8_1}, $json_file, E('/bar', "Not in enum list: $with_replacement_char.");
validate_ok {bar => $invalid_utf8_2}, $json_file, E('/bar', "Not in enum list: $with_replacement_char.");
done_testing;
JSON-Validator-5.18/t/jv-formats.t 0000644 0000765 0000024 00000032323 15210277065 016562 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use Mojo::Util 'decode';
my $schema = {type => 'object', properties => {v => {type => 'string'}}};
subtest 'byte' => sub {
local $schema->{properties}{v}{format} = 'byte';
validate_ok {v => 'amh0aG9yc2Vu'}, $schema;
validate_ok {v => "\0"}, $schema, E('/v', 'Does not match byte format.');
};
subtest 'date' => sub {
local $schema->{properties}{v}{format} = 'date';
validate_ok {v => '2014-12-09'}, $schema;
validate_ok {v => '0000-00-00'}, $schema, E('/v', 'Month out of range.');
validate_ok {v => '0000-01-00'}, $schema, E('/v', 'Day out of range.');
validate_ok {v => '2014-12-09T20:49:37Z'}, $schema, E('/v', 'Does not match date format.');
validate_ok {v => '0-0-0'}, $schema, E('/v', 'Does not match date format.');
validate_ok {v => '09-12-2014'}, $schema, E('/v', 'Does not match date format.');
validate_ok {v => '09-DEC-2014'}, $schema, E('/v', 'Does not match date format.');
validate_ok {v => '09/12/2014'}, $schema, E('/v', 'Does not match date format.');
};
subtest 'date-time' => sub {
local $schema->{properties}{v}{format} = 'date-time';
validate_ok {v => $_},
$schema
for (
'2017-03-29T23:02:55.831Z', '2017-03-29t23:02:55.01z', '2017-03-29 23:02:55-12:00',
'2016-02-29T23:02:55+05:00', '2006-01-02 15:04:05+23:59', '2006-01-02 15:04:05-23:59'
);
validate_ok {v => "2017-03-29\t23:02:55-12:00"}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29T23:02:55+0:0'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29T23:02:55+123:00'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29\t23:02:55+0:0'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29\t23:02:55+123:00'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29\t23:02:55-12'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29\t23:02:55-12:00'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29T23:02:55-12'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => 'xxxx-xx-xxtxx:xx:xxz'}, $schema, E('/v', 'Does not match date-time format.');
validate_ok {v => '2017-03-29T23:02:60Z'}, $schema, E('/v', 'Second out of range.');
validate_ok {v => '2017-03-29T23:61:55Z'}, $schema, E('/v', 'Minute out of range.');
validate_ok {v => '2017-03-29T24:02:55Z'}, $schema, E('/v', 'Hour out of range.');
validate_ok {v => '2017-03-32T23:02:55Z'}, $schema, E('/v', 'Day out of range.');
validate_ok {v => '2017-02-29T23:02:55Z'}, $schema, E('/v', 'Day out of range.');
validate_ok {v => '2017-02-30T23:02:55Z'}, $schema, E('/v', 'Day out of range.');
validate_ok {v => '2017-03-00T23:02:55Z'}, $schema, E('/v', 'Day out of range.');
validate_ok {v => '2017-00-29T23:02:55Z'}, $schema, E('/v', 'Month out of range.');
validate_ok {v => '2017-13-29T23:02:55Z'}, $schema, E('/v', 'Month out of range.');
validate_ok {v => '2017-03-29T23:02:55+23:60'}, $schema, E('/v', 'Time offset minute out of range.');
validate_ok {v => '2017-03-29T23:02:55+24:00'}, $schema, E('/v', 'Time offset hour out of range.');
};
subtest 'double' => sub {
local $schema->{properties}{v}{format} = 'double';
local $TODO = 'cannot test double, since input is already rounded';
validate_ok {v => '1.1000000238418599085576943252817727625370025634765626'}, $schema;
};
subtest 'duration' => sub {
local $schema->{properties}{v}{format} = 'duration';
validate_ok {v => 'foo'}, $schema, E('/v', 'Does not match duration format.');
validate_ok {v => 'P4Y'}, $schema;
validate_ok {v => 'PT0S'}, $schema;
validate_ok {v => 'P1M'}, $schema;
validate_ok {v => 'PT1M'}, $schema;
validate_ok {v => 'PT0.5M'}, $schema;
validate_ok {v => 'PT0,5M'}, $schema;
validate_ok {v => 'P23DT23H'}, $schema;
validate_ok {v => 'P3Y6M4DT12H30M5S'}, $schema;
};
subtest 'email' => sub {
local $schema->{properties}{v}{format} = 'email';
validate_ok {v => 'jhthorsen@cpan.org'}, $schema;
validate_ok {v => 'foo'}, $schema, E('/v', 'Does not match email format.');
validate_ok {v => '用户@例子.广告'}, $schema, E('/v', 'Does not match email format.');
validate_ok {v => 'wrong@jhthorsen@cpan.org'}, $schema, E('/v', 'Does not match email format.');
validate_ok {v => 'wrong jhthorsen@cpan.org'}, $schema, E('/v', 'Does not match email format.');
};
subtest 'float' => sub {
local $schema->{properties}{v}{format} = 'float';
validate_ok {v => '-1.10000002384186'}, $schema;
validate_ok {v => '1.10000002384186'}, $schema;
};
subtest 'int32' => sub {
local $schema->{properties}{v}{format} = 'int32';
validate_ok {v => '-2147483648'}, $schema;
validate_ok {v => '2147483647'}, $schema;
validate_ok {v => '2147483648'}, $schema, E('/v', 'Does not match int32 format.');
};
subtest 'int64' => sub {
local $schema->{properties}{v}{format} = 'int64';
local $TODO = 'Not a 64 bit Perl' unless JSON::Validator::Formats::IV_SIZE >= 8;
validate_ok {v => '-9223372036854775808'}, $schema;
validate_ok {v => '9223372036854775807'}, $schema;
validate_ok {v => '9223372036854775808'}, $schema, E('/v', 'Does not match int64 format.');
};
subtest 'hostname' => sub {
local $TODO = eval 'require Data::Validate::Domain;1' ? undef : 'Missing module';
local $schema->{properties}{v}{format} = 'hostname';
validate_ok {v => 'mojolicio.us'}, $schema;
validate_ok {v => '[]'}, $schema, E('/v', 'Does not match hostname format.');
};
subtest 'idn-email' => sub {
validate_ok {v => decode('UTF-8', '用户@例子.广告')}, $schema;
local $TODO = eval 'require Net::IDN::Encode;1' ? undef : 'Missing module';
local $schema->{properties}{v}{format} = 'idn-email';
validate_ok {v => decode('UTF-8', '用户@')}, $schema, E('/v', 'Does not match idn-email format.');
# --- Valid IDN Email Addresses (Domain Variations) ---
validate_ok {v => decode('UTF-8', 'user@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', 'user@xn--bcher-kva.example')}, $schema;
validate_ok {v => decode('UTF-8', 'test.user@δοκιμή.example')}, $schema;
validate_ok {v => decode('UTF-8', 'info@例.jp')}, $schema;
validate_ok {v => decode('UTF-8', 'john.doe@例子.com')}, $schema;
validate_ok {v => decode('UTF-8', 'user@müller.com')}, $schema;
validate_ok {v => decode('UTF-8', 'user@test-bücher.com')}, $schema;
validate_ok {v => decode('UTF-8', 'user@xn--test-bcher-kvb.com')}, $schema;
# --- Valid IDN Email Addresses (Local-Part Variations) ---
validate_ok {v => decode('UTF-8', 'simple@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', 'user.name@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', 'user_name@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', 'user-name@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', 'user+alias@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', '!#$%&\'*+-/=?^_{}|~@bücher.example')}, $schema;
validate_ok {v => decode('UTF-8', '佐藤@例.jp')}, $schema;
validate_ok {v => decode('UTF-8', 'пользователь@пример.рф')}, $schema;
validate_ok {v => decode('UTF-8', 'user.佐藤@bücher.example')}, $schema;
# --- Invalid IDN Email Addresses (Domain Variations) ---
validate_ok {v => decode('UTF-8', 'user@bücher-.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user@-bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user@bücher..example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user@bücher_example.com')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user@bücher.com.')}, $schema, E('/v', 'Does not match idn-email format.');
# --- Invalid IDN Email Addresses (Local-Part Variations) ---
validate_ok {v => decode('UTF-8', '.user@bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user..name@bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user name@bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user,@bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', 'user(comment)@bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
validate_ok {v => decode('UTF-8', '"user.name@bücher.example')}, $schema, E('/v', 'Does not match idn-email format.');
};
subtest 'idn-hostname' => sub {
local $schema->{properties}{v}{format} = 'idn-hostname';
validate_ok {v => decode('UTF-8', '例子.广告')}, $schema;
};
subtest 'iri' => sub {
local $schema->{properties}{v}{format} = 'iri';
validate_ok {v => 'http://mojolicio.us/?ø=123'}, $schema;
validate_ok {v => decode('UTF-8', 'https://例子.广告/Ῥόδος')}, $schema;
validate_ok {v => '/Ῥόδος'}, $schema, E('/v', 'Scheme missing.');
};
subtest 'iri-reference' => sub {
local $schema->{properties}{v}{format} = 'iri-reference';
validate_ok {v => '/Ῥόδος'}, $schema;
validate_ok {v => 'Ῥόδος'}, $schema;
validate_ok {v => 'http:///Ῥόδος'}, $schema,;
};
subtest 'ipv4' => sub {
local $TODO = eval 'require Data::Validate::IP;1' ? undef : 'Missing module';
local $schema->{properties}{v}{format} = 'ipv4';
validate_ok {v => '255.100.30.1'}, $schema;
validate_ok {v => '300.0.0.0'}, $schema, E('/v', 'Does not match ipv4 format.');
};
subtest 'ipv6' => sub {
local $TODO = eval 'require Data::Validate::IP;1' ? undef : 'Missing module';
local $schema->{properties}{v}{format} = 'ipv6';
validate_ok {v => '::1'}, $schema;
validate_ok {v => '300.0.0.0'}, $schema, E('/v', 'Does not match ipv6 format.');
};
subtest 'json-pointer' => sub {
local $schema->{properties}{v}{format} = 'json-pointer';
validate_ok {v => ''}, $schema;
validate_ok {v => '/foo/bar'}, $schema;
validate_ok {v => 'foo/bar'}, $schema, E('/v', 'Does not match json-pointer format.');
};
subtest 'regex' => sub {
local $schema->{properties}{v}{format} = 'regex';
validate_ok {v => '(\w+)'}, $schema;
validate_ok {v => '(\w'}, $schema, E('/v', 'Does not match regex format.');
};
subtest 'relative-json-pointer' => sub {
local $schema->{properties}{v}{format} = 'relative-json-pointer';
validate_ok {v => '0'}, $schema;
validate_ok {v => '42#'}, $schema;
validate_ok {v => '100/foo/bar'}, $schema;
validate_ok {v => '#'}, $schema, E('/v', 'Relative JSON Pointer must start with a non-negative-integer.');
validate_ok {v => '42foo/bar'}, $schema, E('/v', 'Does not match relative-json-pointer format.');
};
subtest 'time' => sub {
local $schema->{properties}{v}{format} = 'time';
validate_ok {v => $_}, $schema for qw(23:02:55.831Z 23:02:55.01z 23:02:55-12:00 23:02:55+05:00);
validate_ok {v => 'xx:xx:xxz'}, $schema, E('/v', 'Does not match time format.');
validate_ok {v => '23:02:60Z'}, $schema, E('/v', 'Second out of range.');
validate_ok {v => '23:61:55Z'}, $schema, E('/v', 'Minute out of range.');
validate_ok {v => '24:02:55Z'}, $schema, E('/v', 'Hour out of range.');
};
subtest 'uri' => sub {
local $schema->{properties}{v}{format} = 'uri';
validate_ok {v => '//example.com/no-scheme'}, $schema, E('/v', 'Scheme missing.');
validate_ok {v => ''}, $schema, E('/v', 'Scheme, path or fragment are required.');
validate_ok {v => '0://mojolicio.us/?x=123'}, $schema, E('/v', 'Scheme must begin with a letter.');
validate_ok {v => 'http://example.com/%z'}, $schema, E('/v', 'Invalid hex escape.');
validate_ok {v => 'http://example.com/%a'}, $schema, E('/v', 'Hex escapes are not complete.');
validate_ok {v => 'http:////'}, $schema, E('/v', 'Path cannot not start with //.');
validate_ok {v => 'http://mojolicio.us/?x=123'}, $schema;
validate_ok {v => '/relative-path'}, $schema;
validate_ok {v => 'relative-path'}, $schema;
};
subtest 'uri-reference' => sub {
local $schema->{properties}{v}{format} = 'uri-reference';
validate_ok {v => 'http:///whatever'}, $schema;
validate_ok {v => '/relative-path'}, $schema;
validate_ok {v => 'relative-path'}, $schema;
};
subtest 'uri-template' => sub {
local $schema->{properties}{v}{format} = 'uri-template';
validate_ok {v => 'http://mojolicio.us/?x={x}'}, $schema;
};
subtest 'unknown' => sub {
my $warn = 'no warnings seen';
local $SIG{__WARN__} = sub { $warn = shift };
local $schema->{properties}{v}{format} = 'unknown';
validate_ok {v => 'whatever'}, $schema;
like $warn, qr{Format rule for 'unknown' is missing}, 'unknown format cause a warning';
};
subtest 'uuid' => sub {
local $schema->{properties}{v}{format} = 'uuid';
validate_ok {v => '5782165B-6BB6-A72F-B3DD-369D707D6C72'}, $schema, E('/v', 'Does not match uuid format.');
validate_ok {v => '5782165B-6BB6-472F-B3DD-369D707D6C72'}, $schema;
};
done_testing;
JSON-Validator-5.18/t/deep-mixed-ref.t 0000644 0000765 0000024 00000001211 15210277065 017255 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::File 'path';
use Test::More;
my $workdir = path(__FILE__)->dirname;
my $file = path($workdir, 'spec', 'with-deep-mixed-ref.json');
my $jv = JSON::Validator->new(cache_paths => [])->schema($file);
my @errors = $jv->validate({age => 1, weight => {mass => 72, unit => 'kg'}, height => 100});
is int(@errors), 0, 'valid input';
use Mojolicious::Lite;
push @{app->static->paths}, $workdir;
$jv->ua(app->ua);
$jv->schema(app->ua->server->url->clone->path('/spec/with-relative-ref.json'));
@errors = $jv->validate({age => 'not a number'});
is int(@errors), 1, 'invalid age';
done_testing;
JSON-Validator-5.18/t/test/ 0000755 0000765 0000024 00000000000 15211411467 015256 5 ustar jhthorsen staff JSON-Validator-5.18/t/test/object.pm 0000644 0000765 0000024 00000010556 15210277065 017074 0 ustar jhthorsen staff package t::test::object;
use t::Helper;
sub additional_properties {
my $schema = {properties => {number => {type => 'number'}}, additionalProperties => false};
schema_validate_ok {direction => 'NW', foo => 'nope', number => 1600}, $schema,
E('/', 'Properties not allowed: direction, foo.');
$schema->{additionalProperties} = {type => 'string'};
schema_validate_ok {number => 1600, foo => 'nope'}, $schema;
}
sub basic {
my $schema = {type => 'object'};
schema_validate_ok {mynumber => 1}, $schema;
schema_validate_ok [1], $schema, E('/', 'Expected object - got array.');
}
sub dependencies {
my $schema = {
dependencies => {credit_card => ['billing_address']},
properties =>
{name => {type => 'string'}, credit_card => {type => 'number'}, billing_address => {type => 'string'}},
};
schema_validate_ok {name => 'John Doe'}, $schema;
schema_validate_ok {name => 'John Doe', billing_address => '123 Main St'}, $schema;
schema_validate_ok {name => 'John Doe', credit_card => 5555555555555555}, $schema,
E('/billing_address', 'Missing property. Dependee: credit_card.');
}
sub dependent_required {
my $schema = {
dependentRequired => {credit_card => ['billing_address']},
properties =>
{name => {type => 'string'}, credit_card => {type => 'number'}, billing_address => {type => 'string'}},
};
schema_validate_ok {name => 'John Doe', credit_card => 5555555555555555}, $schema,
E('/billing_address', 'Missing property. Dependee: credit_card.');
}
sub dependent_schemas {
my $schema = {
dependentSchemas => {credit_card => ['billing_address']},
properties =>
{name => {type => 'string'}, credit_card => {type => 'number'}, billing_address => {type => 'string'}},
};
schema_validate_ok {name => 'John Doe', credit_card => 5555555555555555}, $schema,
E('/billing_address', 'Missing property. Dependee: credit_card.');
}
sub min_max {
my $schema = {minProperties => 2, maxProperties => 3};
schema_validate_ok {}, {minProperties => 1}, E('/', 'Not enough properties: 0/1.');
schema_validate_ok {a => 1}, $schema, E('/', 'Not enough properties: 1/2.');
schema_validate_ok {a => 1, b => 2}, $schema;
schema_validate_ok {a => 1, b => 2, c => 3}, $schema;
schema_validate_ok {a => 1, b => 2, c => 3, d => 4}, $schema, E('/', 'Too many properties: 4/3.');
}
sub names {
my $schema = {propertyNames => {minLength => 3, maxLength => 5}};
schema_validate_ok {name => 'John', surname => 'Doe'}, $schema,
E('/', '/propertyName/surname String is too long: 7/5.');
$schema->{propertyNames}{maxLength} = 7;
schema_validate_ok {name => 'John', surname => 'Doe'}, $schema;
$schema = {
type => 'object',
propertyNames =>
{anyOf => [{type => 'string', enum => ['foo', 'bar', 'baz']}, {type => 'string', enum => ['hello']}]},
};
schema_validate_ok {FOO => 1}, $schema, E('/', '/propertyName/FOO /anyOf/0 Not in enum list: foo, bar, baz.'),
E('/', '/propertyName/FOO /anyOf/1 Not in enum list: hello.');
schema_validate_ok {foo => 1}, $schema;
}
sub pattern_properties {
my $schema = {patternProperties => {'^S_' => {type => 'string'}, '^I_' => {type => 'integer'}}};
schema_validate_ok {'S_25' => 'This is a string', 'I_0' => 42}, $schema;
schema_validate_ok {'S_0' => 42}, $schema, E('/S_0', 'Expected string - got number.');
}
sub properties {
my $schema = {
properties => {
number => {type => 'number'},
street_name => {type => 'string'},
street_type => {type => 'string', enum => ['Street', 'Avenue', 'Boulevard']}
}
};
schema_validate_ok {number => 1600, street_name => 'Pennsylvania', street_type => 'Avenue'}, $schema;
schema_validate_ok {number => '1600'}, $schema, E('/number', 'Expected number - got string.');
schema_validate_ok {number => 1600, street_name => 'Pennsylvania', street_type => 'Avenue', direction => 'NW'},
$schema;
$schema->{required} = ['number', 'street_name'];
validate_ok {number => 1600, street_type => 'Avenue'}, $schema, E('/street_name', 'Missing property.');
}
sub unevaluated_properties {
local $TODO = 'https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.3.2.4';
my $schema = {properties => {number => {type => 'number'}}, unevaluatedProperties => false};
schema_validate_ok {direction => 'NW', foo => 'nope', number => 1600}, $schema,
E('/', 'Properties not allowed: direction, foo.');
}
1;
JSON-Validator-5.18/t/test/number.pm 0000644 0000765 0000024 00000000667 15210277065 017120 0 ustar jhthorsen staff package t::test::number;
use t::Helper;
sub basic {
schema_validate_ok 1, {type => 'number'};
}
sub maximum {
schema_validate_ok 0, {maximum => 0};
schema_validate_ok 1, {maximum => 1};
schema_validate_ok - 1, {maximum => -2}, E('/', '-1 > maximum(-2)');
}
sub minimum {
schema_validate_ok 0, {minimum => 0};
schema_validate_ok 1, {minimum => 1};
schema_validate_ok - 2, {minimum => -1}, E('/', '-2 < minimum(-1)');
}
1;
JSON-Validator-5.18/t/test/array.pm 0000644 0000765 0000024 00000004545 15210277065 016745 0 ustar jhthorsen staff package t::test::array;
use t::Helper;
sub additional_items {
my $schema = {
type => 'array',
additionalItems => false,
items => [
{type => 'number'},
{type => 'string'},
{type => 'string', enum => ['Street', 'Avenue', 'Boulevard']},
{type => 'string', enum => ['NW', 'NE', 'SW', 'SE']}
]
};
validate_ok [1600, 'Pennsylvania', 'Avenue', 'NW', 'Washington'], $schema, E('/', 'Invalid number of items: 5/4.');
}
sub basic {
my $schema = {type => 'array'};
schema_validate_ok [], $schema;
schema_validate_ok {}, $schema, E('/', 'Expected array - got object.');
}
sub contains {
my $schema = {type => 'array', contains => {type => 'string', enum => ['NW']}};
schema_validate_ok [1600, 'NW'], $schema;
$schema->{contains}{enum} = ['Nope'];
schema_validate_ok [1600, 'NW'], $schema, E('/0', 'Expected string - got number.'),
E('/1', 'Not in enum list: Nope.');
}
sub items {
my $schema = {type => 'array', items => {type => 'number'}};
validate_ok [1], $schema;
validate_ok [1, 'foo'], $schema, E('/1', 'Expected number - got string.');
$schema->{items} = {};
validate_ok [1, 'foo', 1.2], $schema;
$schema->{items} = true;
validate_ok [1, 'foo', 1.2], $schema;
}
sub min_max {
my $schema = {type => 'array', minItems => 2, maxItems => 2};
schema_validate_ok [1], $schema, E('/', 'Not enough items: 1/2.');
schema_validate_ok [1, 2], $schema;
schema_validate_ok [1, 2, 3], $schema, E('/', 'Too many items: 3/2.');
}
sub min_max_contains {
my $schema = {type => 'array', contains => {type => 'string'}, maxContains => 3, minContains => 2};
schema_validate_ok [qw(A)], $schema, E('/', 'Contains not enough items: 1/2.');
schema_validate_ok [qw(A B C D)], $schema, E('/', 'Contains too many items: 4/3.');
schema_validate_ok [qw(A B)], $schema;
schema_validate_ok [qw(A B C)], $schema;
}
sub unevaluated_items {
local $TODO = 'https://json-schema.org/draft/2019-09/json-schema-core.html#unevaluatedItems';
my $schema = {unevaluatedItems => {}};
validate_ok [1600, 'Pennsylvania', 'Avenue', 'NW', 'Washington'], $schema, E('/', 'Invalid number of items: 5/4.');
}
sub unique {
my $schema = {type => 'array', uniqueItems => 1, items => {type => 'integer'}};
validate_ok [123, 124], $schema;
validate_ok [1, 2, 1], $schema, E('/', 'Unique items required.');
}
1;
JSON-Validator-5.18/t/jv-array.t 0000644 0000765 0000024 00000010333 15210277065 016222 0 ustar jhthorsen staff use lib '.';
use Mojo::Base -strict;
use Mojo::JSON 'encode_json';
use t::Helper;
my $simple = {type => 'array', items => {type => 'number'}};
my $length = {type => 'array', minItems => 2, maxItems => 2};
my $unique = {type => 'array', uniqueItems => 1, items => {type => 'integer'}};
my $tuple = {
type => 'array',
items => [
{type => 'number'},
{type => 'string'},
{type => 'string', enum => ['Street', 'Avenue', 'Boulevard']},
{type => 'string', enum => ['NW', 'NE', 'SW', 'SE']}
]
};
validate_ok [1], $simple;
validate_ok [1, 'foo'], $simple, E('/1', 'Expected number - got string.');
validate_ok [1], $length, E('/', 'Not enough items: 1/2.');
validate_ok [1, 2], $length;
validate_ok [1, 2, 3], $length, E('/', 'Too many items: 3/2.');
validate_ok [123, 124], $unique;
validate_ok [1, 2, 1], $unique, E('/', 'Unique items required.');
validate_ok [1600, 'Pennsylvania', 'Avenue', 'NW'], $tuple;
validate_ok [24, 'Sussex', 'Drive'], $tuple, E('/2', 'Not in enum list: Street, Avenue, Boulevard.');
validate_ok [10, 'Downing', 'Street'], $tuple;
validate_ok [1600, 'Pennsylvania', 'Avenue', 'NW', 'Washington'], $tuple;
$tuple->{additionalItems} = Mojo::JSON->false;
validate_ok [1600, 'Pennsylvania', 'Avenue', 'NW', 'Washington'], $tuple, E('/', 'Invalid number of items: 5/4.');
validate_ok [1600, 'NW'], {type => 'array', contains => {type => 'string', enum => ['NW']}};
validate_ok [1600, 'NW'], {type => 'array', contains => {type => 'string', enum => ['Nope']}},
E('/0', 'Expected string - got number.'), E('/1', 'Not in enum list: Nope.');
# Make sure all similar numbers gets converted from strings
my $jv = JSON::Validator->new->coerce('numbers');
my @numbers;
$jv->schema({type => 'array', items => {type => 'number'}});
@numbers = qw(1.42 2.3 1.42 1.42);
ok !$jv->validate(\@numbers), 'numbers are valid';
is encode_json(\@numbers), encode_json([1.42, 2.3, 1.42, 1.42]), 'coerced into integers';
$jv->schema({type => 'array', items => {type => 'integer'}});
@numbers = qw(1 2 1 1 3 1);
ok !$jv->validate(\@numbers), 'integers are valid';
is encode_json(\@numbers), encode_json([1, 2, 1, 1, 3, 1]), 'coerced into numbers';
my $array_constant = {type => 'array', const => [1, 'a', undef]};
validate_ok [1, 'a', undef], $array_constant;
validate_ok [1, 'b', undef], $array_constant, E('/', q{Does not match const: [1,"a",null].});
# TODO! true, false are draft 6+ only
validate_ok [1, 'foo', 1.2], {type => 'array', items => {}};
validate_ok [1, 'foo', 1.2], {type => 'array', items => true};
validate_ok [1, 'foo', 1.2], {type => 'array', items => [true, true, false]}, E('/2', 'Should not match.');
validate_ok [1, 'foo', 1.2], {type => 'array', items => false}, E('/0', 'Should not match.'),
E('/1', 'Should not match.'), E('/2', 'Should not match.');
validate_ok [1, 'foo', 1.2],
{definitions => {my_true_ref => true}, type => 'array', items => {'$ref' => '#/definitions/my_true_ref'}};
validate_ok [1, 'foo', 1.2],
{definitions => {my_false_ref => false}, type => 'array', items => {'$ref' => '#/definitions/my_false_ref'}},
E('/0', 'Should not match.'), E('/1', 'Should not match.'), E('/2', 'Should not match.');
validate_ok [1, 'foo', 1.2],
{
definitions => {my_true_ref => true, my_false_ref => false},
type => 'array',
items => [
{'$ref' => '#/definitions/my_true_ref'},
{'$ref' => '#/definitions/my_true_ref'},
{'$ref' => '#/definitions/my_false_ref'}
],
},
E('/2', 'Should not match.');
validate_ok [], {type => 'array', contains => {const => 'foo'}}, E('/', 'No items contained.');
validate_ok [1], {contains => {const => 'foo'}}, E('/0', 'Does not match const: "foo".');
validate_ok [1], {items => {not => {}}}, E('/0', 'Should not match.');
validate_ok [1], {items => false}, E('/0', 'Should not match.');
validate_ok [1, 2], {contains => {not => {}}}, E('/0', 'Should not match.'), E('/1', 'Should not match.');
validate_ok [1, 2], {contains => false}, E('/0', 'Should not match.'), E('/1', 'Should not match.');
validate_ok [1, 'hello'], {contains => {const => 1}, items => {type => 'number'}},
E('/1', 'Expected number - got string.');
validate_ok [1, 'hello'], {contains => {const => 1}, items => [{type => 'string'}]},
E('/0', 'Expected string - got number.');
done_testing;
JSON-Validator-5.18/t/issue-272-recursive-data-protection.t 0000644 0000765 0000024 00000003152 15211411416 023217 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv3;
use Mojo::JSON qw(true);
use Test::More;
# Regression test for GH #272 ("heisenbug with openapi 3.1.* schema").
#
# Validating an OpenAPI 3.1 document whose parameter uses a $ref schema
# intermittently produced a false "/components/parameters/Id/$ref: Missing
# property." error. Root cause: recursive_data_protection's "seen" cache keyed on
# stringified memory addresses ("$schema:$data"); transient merged-schema hashrefs
# (built while following the meta-schema's $refs) were freed mid-validation and
# their addresses reused, colliding with stale cached error lists.
#
# It is a heisenbug (address reuse, independent of the hash seed), so we validate
# many times and assert the document is *always* valid.
sub spec {
return {
openapi => '3.1.0',
info => {version => '1.0', title => 'whatever'},
components => {
parameters => {
Id => {in => 'path', name => 'id', required => true, schema => {'$ref' => '#/components/schemas/IdType'}},
},
schemas => {IdType => {type => 'integer'}},
},
};
}
my $spec_url = 'https://spec.openapis.org/oas/3.1/schema/2021-05-20';
my $iterations = 500;
my (%errors, $failures);
for (1 .. $iterations) {
my $schema = JSON::Validator::Schema::OpenAPIv3->new(spec(), specification => $spec_url);
next unless my @e = @{$schema->errors};
$failures++;
$errors{"$_"}++ for @e;
}
is $failures, undef, "valid 3.1 \$ref parameter stays valid across $iterations validations (#272)"
or diag "spurious errors: " . join(', ', map {"$_ x$errors{$_}"} sort keys %errors);
done_testing;
JSON-Validator-5.18/t/bundle.t 0000644 0000765 0000024 00000010054 15210277065 015740 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use JSON::Validator::Schema::Draft7;
use Mojo::File 'path';
use Test::More;
my $workdir = path(__FILE__)->to_abs->dirname;
my $jv = JSON::Validator->new;
subtest 'Run multiple times to make sure _reset() works' => sub {
for my $n (1 .. 3) {
my $bundled = $jv->schema({
id => 'https://jv.example.com/reset',
surname => {'$ref' => '#/definitions/name'},
age => {'$ref' => 'b.json#/definitions/years'},
definitions => {name => {type => 'string'}},
B => {id => 'b.json', definitions => {years => {type => 'integer'}}},
})->bundle;
ok $bundled->{definitions}{name}, "[$n] definitions/name still in definitions";
is $bundled->{definitions}{name}{type}, 'string', "[$n] definitions/name/type still in definitions";
is $bundled->{definitions}{'b_json-definitions_years'}{type}, 'integer', "[$n] added to definitions";
isnt $bundled->{age}, $jv->schema->get('/age'), "[$n] new age ref";
is $bundled->{age}{'$ref'}, '#/definitions/b_json-definitions_years', "[$n] age \$ref";
is $bundled->{surname}{'$ref'}, '#/definitions/name', "[$n] surname \$ref";
}
};
subtest 'check bundled structure' => sub {
is $jv->get([qw(surname type)]), 'string', 'get /surname/$ref';
is $jv->get('/surname/type'), 'string', 'get /surname/type';
is $jv->get('/surname/$ref'), '#/definitions/name', 'get /surname/$ref';
is $jv->schema->get('/surname/type'), 'string', 'schema get /surname/type';
is $jv->schema->data->{surname}{'$ref'}, '#/definitions/name', 'schema get /surname/$ref';
my $bundled = $jv->schema('data://main/bundled.json')->bundle;
is_deeply [sort keys %{$bundled->{definitions}}], ['objtype'], 'no dup definitions';
};
subtest 'definitions in disk spec' => sub {
for my $path (
['test-definitions-key.json'],
['with-deep-mixed-ref.json'],
['with-deep-mixed-ref.json'],
[File::Spec->updir, 'spec', 'with-deep-mixed-ref.json'],
)
{
my $file = path $workdir, 'spec', @$path;
my @expected = qw(age_json height unit_json weight_json);
my $bundled = $jv->schema($file)->bundle;
is_deeply [sort keys %{$bundled->{definitions}}], \@expected, "right definitions in disk spec @$path"
or diag join ', ', sort keys %{$bundled->{definitions}};
}
};
subtest 'ensure filenames with funny characters not mangled by Mojo::URL' => sub {
my $file3 = path $workdir, 'spec', 'space bundle.json';
my $bundled = eval { $jv->schema($file3)->bundle };
is $@, '', 'loaded absolute filename with space';
is $bundled->{definitions}{space_age_json}{description}, 'Age in years', 'space_age_json def';
is $bundled->{properties}{age}{'$ref'}, '#/definitions/space_age_json', 'space_age_json ref';
};
subtest 'extract subset of schema' => sub {
my $bundled = $jv->schema('data://main/bundled.json')->bundle({schema => $jv->get([qw(paths /withdots get)])});
is_deeply(
$bundled,
{
definitions => {objtype => {properties => {propname => {type => 'string'}}, type => 'object'}},
responses => {200 => {schema => {'$ref' => '#/definitions/objtype'}}}
},
'subset of schema was bundled'
) or diag explain $bundled;
};
subtest 'no leaking path' => sub {
my $bundled = $jv->schema('data://main/bundled.json')->bundle({schema => $jv->get([qw(paths /withdots get)])});
my $ref_name_prefix = $workdir;
$ref_name_prefix =~ s![^\w-]!_!g;
$jv->schema(path $workdir, 'spec', 'bundle-no-leaking-filename.json');
my @definitions = keys %{$bundled->{definitions}};
ok @definitions, 'definitions are present';
is_deeply [grep { 0 == index $_, $ref_name_prefix } @definitions], [], 'no leaking of path';
};
done_testing;
__DATA__
@@ bundled.json
{
"definitions": {
"objtype": {
"type": "object",
"properties": {"propname": {"type": "string"}}
}
},
"paths": {
"/withdots": {
"get": {
"responses": {
"200": {"schema": {"$ref": "#/definitions/objtype"}}
}
}
}
}
}
JSON-Validator-5.18/t/openapiv2-readonly.t 0000644 0000765 0000024 00000002715 15210277065 020212 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my ($body, @errors);
$body = sub { +{exists => 1, value => {}} };
@errors = $schema->validate_request([post => '/user'], {body => $body});
is "@errors", '', 'required is ignored on validate_request';
@errors = $schema->validate_response([post => '/user'], {body => $body});
is "@errors", '/body/age: Missing property.', 'age is required in response';
$body = sub { +{exists => 1, value => {age => 42}} };
@errors = $schema->validate_request([post => '/user'], {body => $body});
is "@errors", '/body/age: Read-only.', 'age is read-only for request';
@errors = $schema->validate_response([post => '/user'], {body => $body});
is "@errors", '', 'age is present in response';
done_testing;
__DATA__
@@ spec.json
{
"swagger": "2.0",
"info": {"version": "", "title": "Test readonly"},
"basePath": "/api",
"paths": {
"/user": {
"post": {
"parameters": [
{"name":"body", "in":"body", "schema": {"$ref": "#/definitions/User"}}
],
"responses": {
"200": { "description": "ok", "schema": {"$ref": "#/definitions/User"}}
}
}
}
},
"definitions": {
"age": {
"type": "integer",
"readOnly": true
},
"User": {
"type": "object",
"required": ["age"],
"properties": {
"age": {"$ref": "#/definitions/age"}
}
}
}
}
JSON-Validator-5.18/t/openapiv3-discriminator.t 0000644 0000765 0000024 00000006150 15210277065 021242 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my ($body, @errors);
my %cat = (name => 'kit-e-cat', petType => 'cat', huntingSkill => 'adventurous');
my %dog = (name => 'dog-e-dog', petType => 'dog', packSize => 4);
$body = sub { {exists => 1, value => {%cat, petType => 'dog'}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body/packSize: /allOf/1 Missing property.', 'invalid dog';
$body = sub { {exists => 1, value => {%cat}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '', 'valid cat';
$body = sub { {exists => 1, value => {%dog, petType => 'cat'}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body/huntingSkill: /allOf/1 Missing property.', 'invalid cat';
$body = sub { {exists => 1, value => {%dog}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '', 'valid dog';
$body = sub { {exists => 1, value => {%dog, petType => ''}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body: Discriminator petType has no value.', 'discriminator is required';
$body = sub { {exists => 1, value => {%cat, petType => 'Hamster'}} };
@errors = $schema->validate_request([post => '/pets'], {body => $body});
is "@errors", '/body: No definition for discriminator Hamster.', 'invalid discriminator';
done_testing;
__DATA__
@@ spec.json
{
"openapi": "3.0.0",
"info": { "title": "Discriminator", "version": "" },
"paths": {
"/pets": {
"post": {
"requestBody": {
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } }
}
}
}
},
"components": {
"schemas": {
"Pet": {
"type": "object",
"discriminator": {
"propertyName": "petType",
"mapping": {
"cat": "#/components/schemas/Cat",
"dog": "#/components/schemas/Dog"
}
},
"required": ["name", "petType"],
"properties": {
"name": {"type": "string"},
"petType": {"type": "string"}
}
},
"Cat": {
"description": "A representation of a cat",
"allOf": [
{"$ref": "#/components/schemas/Pet"},
{
"type": "object",
"required": ["huntingSkill"],
"properties": {"huntingSkill": {"type": "string", "description": "The measured skill for hunting", "default": "lazy", "enum": ["clueless", "lazy", "adventurous", "aggressive"]}
}
}
]
},
"Dog": {
"description": "A representation of a dog",
"allOf": [
{"$ref": "#/components/schemas/Pet"},
{
"type": "object",
"required": ["packSize"],
"properties": {
"packSize": {"type": "integer", "format": "int32", "description": "the size of the pack the dog is from", "default": 0, "minimum": 0}
}
}
]
}
}
}
}
JSON-Validator-5.18/t/jv-oneof.t 0000644 0000765 0000024 00000006217 15210277065 016220 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema = {oneOf => [{type => 'string', maxLength => 5}, {type => 'number', minimum => 0}]};
validate_ok 'short', $schema;
validate_ok 12, $schema;
$schema = {oneOf => [{type => 'number', multipleOf => 5}, {type => 'number', multipleOf => 3}]};
validate_ok 10, $schema;
validate_ok 9, $schema;
validate_ok 15, $schema, E('/', 'All of the oneOf rules match.');
validate_ok 13, $schema, E('/', '/oneOf/0 Not multiple of 5.'), E('/', '/oneOf/1 Not multiple of 3.');
$schema = {oneOf => [{type => 'object'}, {type => 'string', multipleOf => 3}]};
validate_ok 13, $schema, E('/', '/oneOf Expected object/string - got number.');
$schema = {oneOf => [{type => 'object'}, {type => 'number', multipleOf => 3}]};
validate_ok 13, $schema, E('/', '/oneOf/0 Expected object - got number.'), E('/', '/oneOf/1 Not multiple of 3.');
# Alternative oneOf
# https://json-schema.org/draft-07/json-schema-validation.html#rfc.section.7
$schema = {type => 'object', properties => {x => {type => ['string', 'null'], format => 'date-time'}}};
validate_ok {x => 'foo'}, $schema, E('/x', 'Does not match date-time format.');
validate_ok {x => '2015-04-21T20:30:43.000Z'}, $schema;
validate_ok {x => undef}, $schema;
$schema = {type => 'object', properties => {x => {type => ['null', 'number']}}};
validate_ok {x => 'foo'}, $schema, E('/x', 'Expected null/number - got string.');
validate_ok 1, {oneOf => [{minimum => 1}, {minimum => 2}, {maximum => 3}]}, E('/', 'oneOf rules 0, 2 match.');
validate_ok 'hello', {oneOf => [true, false]};
validate_ok 'hello', {oneOf => [true, true]}, E('/', 'All of the oneOf rules match.');
validate_ok 'hello', {oneOf => [false, false]}, E('/', '/oneOf/0 Should not match.'),
E('/', '/oneOf/1 Should not match.');
validate_ok 'hello', {oneOf => [true, {type => ['string', 'boolean']}]}, E('/', 'All of the oneOf rules match.');
validate_ok 'hello', {type => ['integer', 'boolean']}, E('/', 'Expected integer/boolean - got string.');
validate_ok 'hello', {oneOf => [false, {type => ['integer', 'string'], enum => [123, 'HELLO']}]},
E('/', '/oneOf/0 Should not match.'), E('/', '/oneOf/1 Not in enum list: 123, HELLO.');
validate_ok 'hello', {oneOf => [false, {type => ['integer', 'boolean']}]}, E('/', '/oneOf/0 Should not match.'),
E('/', '/oneOf/1 Expected integer/boolean - got string.');
validate_ok 'hello', {oneOf => [false, {type => 'integer'}]}, E('/', '/oneOf/0 Should not match.'),
E('/', '/oneOf/1 Expected integer - got string.');
validate_ok 'hello', {oneOf => [{type => ['integer', 'boolean']}]},
E('/', '/oneOf/0 Expected integer/boolean - got string.');
validate_ok 'hello',
{oneOf => [{oneOf => [{type => 'boolean'}, {type => 'string', maxLength => 2}]}, {type => 'integer'}]},
E('/', '/oneOf/0/oneOf/0 Expected boolean - got string.'), E('/', '/oneOf/0/oneOf/1 String is too long: 5/2.'),
E('/', '/oneOf/1 Expected integer - got string.');
validate_ok {foo => 'not an arrayref'},
{oneOf => [{type => 'object', properties => {foo => {type => 'array'}}}, {type => 'boolean'}]},
E('/foo', '/oneOf/0 Expected array - got string.'), E('/', '/oneOf/1 Expected boolean - got object.');
done_testing;
JSON-Validator-5.18/t/load-data.t 0000644 0000765 0000024 00000003061 15210277065 016315 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $jv = JSON::Validator->new;
my @errors = $jv->schema('data://main/spec.json')->validate({firstName => 'yikes!'});
is int(@errors), 1, 'one error';
is $errors[0]->path, '/lastName', 'lastName';
is $errors[0]->message, 'Missing property.', 'required';
is_deeply $errors[0]->TO_JSON, {path => '/lastName', message => 'Missing property.'}, 'TO_JSON';
use Mojo::File 'path';
push @INC, path(path(__FILE__)->dirname, 'stack')->to_string;
require Some::Module;
eval { Some->validate_age1({age => 1}) };
like $@, qr{age1\.json}, 'could not find age1.json';
ok !Some->validate_age0({age => 1}), 'validate_age0';
ok !Some::Module->validate_age0({age => 1}), 'validate_age0';
ok !Some::Module->validate_age1({age => 1}), 'validate_age1';
eval { Mojolicious::Plugin::TestX->validate('data:///spec.json', {}) };
ok !$@, 'found spec.json in main' or diag $@;
@errors = $jv->schema('data://main/spec.json')->validate({});
like "@errors", qr{firstName.*lastName}, 'required is sorted';
package Mojolicious::Plugin::TestX;
sub validate { $jv->schema($_[1])->validate($_[2]) }
package main;
is_deeply [sort keys %{$jv->store->schemas}], [qw(data:///spec.json data://main/spec.json)], 'schemas in store';
done_testing;
__DATA__
@@ spec.json
{
"title": "Example Schema",
"type": "object",
"required": ["lastName", "firstName"],
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"age": { "type": "integer", "minimum": 0, "description": "トシ" }
}
}
JSON-Validator-5.18/t/uri.t 0000644 0000765 0000024 00000004223 15210277065 015267 0 ustar jhthorsen staff use Mojo::Base -strict;
use Test::More;
use JSON::Validator::URI;
subtest 'url https' => sub {
my $url = JSON::Validator::URI->new('https://foo.com');
is $url->scheme, 'https', 'scheme';
is $url->host, 'foo.com', 'host';
is $url->nid, undef, 'nid';
is $url->nss, undef, 'nss';
};
subtest 'urn uuid' => sub {
my $urn = JSON::Validator::URI->new('urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f');
is $urn->host, undef, 'host';
is $urn->scheme, 'urn', 'scheme';
is $urn->nid, 'uuid', 'nid';
is $urn->nss, 'ee564b8a-7a87-4125-8c96-e9f123d6766f', 'nss';
is $urn->fragment, undef, 'fragment';
};
subtest 'urn jv' => sub {
my $urn = JSON::Validator::URI->new('urn:jv:draft4-4242#foo');
ok $urn->is_abs, 'is_abs';
is $urn->host, undef, 'host';
is $urn->scheme, 'urn', 'scheme';
is $urn->nid, 'jv', 'nid';
is $urn->nss, 'draft4-4242', 'nss';
is $urn->fragment, 'foo', 'fragment';
my $clone = $urn->clone;
is $clone->host, undef, 'clone host';
is $clone->scheme, 'urn', 'clone scheme';
is $clone->nid, 'jv', 'clone nid';
is $clone->nss, 'draft4-4242', 'clone nss';
is $clone->fragment, 'foo', 'clone fragment';
is $clone->to_string, 'urn:jv:draft4-4242#foo', 'clone to_string';
};
subtest 'urn to_abs' => sub {
my $urn = JSON::Validator::URI->new('urn:jv:draft4-4242#foo');
my $abs = $urn->to_abs(JSON::Validator::URI->new('urn:jv:draft4-4242#bar'));
is $abs->to_string, $urn->to_string, 'is_abs';
$urn = JSON::Validator::URI->new('#foo');
$abs = $urn->to_abs(JSON::Validator::URI->new('urn:jv:draft4-4242#bar'));
is $abs->to_string, 'urn:jv:draft4-4242#foo', 'to_abs';
# TODO: I don't think it's valid for a URN to have a path, so this might get reverted
my $rel = JSON::Validator::URI->new('b.json');
is $rel->to_abs($abs)->to_string, 'urn:jv:draft4-4242/b.json#foo', 'b.json to_abs';
};
done_testing;
JSON-Validator-5.18/t/draft4-acceptance.t 0000644 0000765 0000024 00000001102 15210277065 017731 0 ustar jhthorsen staff use lib '.';
use t::Helper;
$ENV{MOJO_LOG_LEVEL} //= 'fatal';
plan skip_all => 'TEST_ACCEPTANCE=1' unless $ENV{TEST_ACCEPTANCE};
delete $ENV{TEST_ACCEPTANCE} if $ENV{TEST_ACCEPTANCE} eq '1';
my @todo_tests;
push @todo_tests, ['id.json', 'id inside an enum is not a real identifier'];
push @todo_tests, ['ref.json', '$ref prevents a sibling id from changing the base uri'];
push @todo_tests, ['refRemote.json', 'Location-independent identifier in remote ref'];
t::Helper->acceptance('JSON::Validator::Schema::Draft4', todo_tests => \@todo_tests);
done_testing;
JSON-Validator-5.18/t/openapiv2-basic.t 0000644 0000765 0000024 00000021463 15210277065 017457 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv2;
use Mojo::JSON qw(false true);
use Test::Deep;
use Test::More;
my $cwd = Mojo::File->new(__FILE__)->dirname;
my $schema = JSON::Validator::Schema::OpenAPIv2->new;
my ($body, @errors);
subtest 'basic' => sub {
is $schema->specification, 'http://swagger.io/v2/schema.json', 'specification';
is_deeply $schema->coerce, {booleans => 1, numbers => 1, strings => 1}, 'default coercion';
eval {
my $s = JSON::Validator->new->schema('data://main/spec-resolve-refs.json')->schema;
is $s->get([qw(paths /user get responses 200 schema type)]), 'object', 'resolved "User"';
1;
} or do {
diag $@;
ok 0, 'Could not resolve "User"';
};
$schema = JSON::Validator->new->schema($cwd->child(qw(spec v2-petstore.json)))->schema;
isa_ok $schema, 'JSON::Validator::Schema::OpenAPIv2';
is_deeply(
$schema->routes->to_array,
[
{method => 'get', operation_id => 'showPetById', path => '/pets/{petId}'},
{method => 'get', operation_id => 'listPets', path => '/pets'},
{method => 'post', operation_id => 'createPets', path => '/pets'},
],
'routes'
);
is_deeply $schema->errors, [], 'errors';
};
subtest base_url => sub {
is $schema->base_url, 'http://petstore.swagger.io/v1', 'get';
is $schema->base_url('https://api.example.com:8080/api'), $schema, 'set url';
is_deeply $schema->get('/schemes'), ['https'], 'schemes changed';
is $schema->get('/host'), 'api.example.com:8080', 'host changed';
is $schema->get('/basePath'), '/api', 'basePath changed';
is $schema->base_url(Mojo::URL->new('//api2.example.com')), $schema, 'set without scheme';
is_deeply $schema->get('/schemes'), ['https'], 'schemes unchanged';
is $schema->get('/host'), 'api2.example.com', 'host changed';
is $schema->get('/basePath'), '/', 'basePath changed';
is $schema->base_url(Mojo::URL->new('/v1')), $schema, 'set path';
is $schema->base_url->to_string, 'https://api2.example.com/v1', 'get';
my $schema_with_port = JSON::Validator->new->schema($cwd->child(qw(spec bundlecheck.json)))->schema;
is $schema_with_port->base_url->host, 'localhost', 'host';
is $schema_with_port->base_url->port, 3000, 'port';
};
subtest 'validate schema' => sub {
@errors = @{JSON::Validator->new->schema({swagger => '2.0', paths => {}})->schema->errors};
is "@errors", '/info: Missing property.', 'invalid schema';
};
subtest 'parameters_for_request' => sub {
is $schema->parameters_for_request([GET => '/pets/nope']), undef, 'no such path';
cmp_deeply(
$schema->parameters_for_request([GET => '/pets']),
[{
default => 20,
description => 'How many items to return at one time (max 100)',
format => 'int32',
in => 'query',
name => 'limit',
required => false,
type => 'integer',
}],
'parameters_for_request inside path'
);
cmp_deeply(
$schema->parameters_for_request([post => '/pets']),
[{in => 'body', name => 'body', accepts => ['application/json'], required => true, schema => ignore, type => ''}],
'parameters_for_request for body'
);
cmp_deeply(
$schema->parameters_for_request([get => '/pets/{petId}']),
[{
description => 'The id of the pet to retrieve',
in => 'path',
name => 'petId',
required => true,
type => 'string',
}],
'parameters_for_request inside method'
);
is $schema->get([qw(paths /pets post parameters 0 accepts)]), undef,
'accepts was not added to schema by parameters_for_request()';
is $schema->get([qw(paths /pets post parameters 0 type)]), undef,
'type was not added to schema by parameters_for_request()';
};
subtest 'parameters_for_response' => sub {
is $schema->parameters_for_response([GET => '/pets/nope']), undef, 'no such path';
cmp_deeply $schema->parameters_for_response([GET => '/pets']),
[
superhashof({in => 'header', name => 'x-next'}),
superhashof({in => 'body', name => 'body', accepts => ['application/json']}),
],
'parameters_for_request inside path and default response code';
cmp_deeply $schema->parameters_for_response([GET => '/pets', 404]),
[superhashof({in => 'body', name => 'body', accepts => ['application/json']})], 'default response';
};
subtest 'validate_request' => sub {
@errors = $schema->validate_request([get => '/pets'], {query => {limit => 10, foo => '42'}});
is "@errors", '', 'limit ok, even as string';
@errors = $schema->validate_request([get => '/pets'], {query => {limit => 'foo'}});
is "@errors", '/limit: Expected integer - got string.', 'limit failed';
$body = {exists => 0};
@errors = $schema->validate_request([POST => '/pets'], {body => \&body});
is "@errors", '/body: Missing property.', 'default content type, but missing body';
is_deeply $body, {exists => 0, in => 'body', name => 'body', valid => 0}, 'input was mutated';
$body = {exists => 1, value => {name => 'kitty'}};
@errors = $schema->validate_request([POST => '/pets'], {body => \&body});
is "@errors", '/body/id: Missing property.', 'missing id in body';
$body = {exists => 1, value => {id => 42, name => 'kitty'}};
@errors = $schema->validate_request([POST => '/pets'], {body => \&body});
is "@errors", '', 'valid request body';
is_deeply $body,
{
content_type => 'application/json',
exists => 1,
in => 'body',
name => 'body',
valid => 1,
value => $body->{value}
},
'input was mutated';
};
subtest 'validate_response' => sub {
$body = {exists => 1, value => {id => 42, name => 'kitty'}};
@errors = $schema->validate_response([POST => '/pets', 201], {});
is "@errors", '', 'valid response body 201';
$body = {exists => 1, value => {code => 42}};
@errors = $schema->validate_response([post => '/pets', 200], {body => \&body});
is "@errors", '/body/message: Missing property.', 'valid response body default';
};
subtest 'validate_response - accept' => sub {
$body = {accept => 'text/plain'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '/header/Accept: Expected application/json - got text/plain.', 'invalid accept';
is_deeply $body, {accept => 'text/plain', content_type => '', in => 'body', name => 'body', valid => 0},
'failed to negotiate content type';
$body = {accept => 'application/*'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '', 'valid accept';
is_deeply $body,
{accept => 'application/*', content_type => 'application/json', in => 'body', name => 'body', valid => 1},
'negotiated content type';
};
subtest 'validate_response - content_type' => sub {
$body = {content_type => 'text/plain'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '/body: Expected application/json - got text/plain.', 'invalid content_type';
is_deeply $body, {content_type => 'text/plain', in => 'body', name => 'body', valid => 0},
'failed to negotiate content type';
$body = {content_type => 'application/json'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '', 'valid content_type';
is_deeply $body,
{content_type => 'application/json', content_type => 'application/json', in => 'body', name => 'body', valid => 1},
'negotiated content type';
};
subtest add_default_response => sub {
$schema = JSON::Validator->new->schema($cwd->child(qw(spec v2-petstore.json)))->schema;
ok !$schema->get('/definitions/DefaultResponse'), 'default response missing';
ok !$schema->get([paths => '/petss', 'get', 'responses', '400']), 'default response missing for 400';
$schema->add_default_response;
ok $schema->get('/definitions/DefaultResponse'), 'default response added';
my $bad_request = $schema->data->{paths}{'/pets'}{get}{responses}{400};
is $bad_request->{description}, 'Default response.', 'bad_request description';
like $bad_request->{schema}{'$ref'}, qr{^file://.*v2-petstore.json\#/definitions/DefaultResponse$}, 'bad_request ref';
for my $status (400, 401, 404, 500, 501) {
ok $schema->get([paths => '/pets', 'get', 'responses', $status]), "default response for $status";
}
delete $schema->{errors};
is_deeply $schema->errors, [], 'errors';
};
done_testing;
sub body {$body}
__DATA__
@@ spec-resolve-refs.json
{
"swagger": "2.0",
"info": {"version": "", "title": "Test non standard refs"},
"basePath": "/api",
"paths": {
"/user": {
"get": {
"responses": {
"200": { "description": "ok", "schema": { "$ref": "User" } }
}
}
}
},
"definitions": {
"User": {
"type": "object",
"properties": {}
}
}
}
JSON-Validator-5.18/t/jv-if-then-else.t 0000644 0000765 0000024 00000001566 15210277065 017374 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema;
$schema = {
if => {properties => {ifx => {type => 'string'}}},
then => {properties => {ifx => {maxLength => 3}}},
else => {properties => {ifx => {type => 'number'}}},
};
validate_ok {ifx => 'foo'}, $schema;
validate_ok {ifx => 'foobar'}, $schema, E('/ifx', 'String is too long: 6/3.');
validate_ok {ifx => 42}, $schema;
validate_ok {ifx => []}, $schema, E('/ifx', 'Expected number - got array.');
$schema = {
type => 'array',
if => {maxItems => 5},
then => {items => {pattern => '^[0-9]$'}},
else => {items => {pattern => '^[a-z]$'}},
};
validate_ok [qw(2 4 7)], $schema;
validate_ok [qw(a 1)], $schema, E('/0', 'String does not match ^[0-9]$.');
validate_ok [qw(6 q a b 8 z)], $schema, E('/0', 'String does not match ^[a-z]$.'),
E('/4', 'String does not match ^[a-z]$.');
done_testing;
JSON-Validator-5.18/t/load-file.t 0000644 0000765 0000024 00000002265 15210277065 016330 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $file = Mojo::File::path(qw(t spec person.json))->to_abs;
my $spec = Mojo::URL->new->scheme('file')->host('')->path(join '/', @$file);
my $jv = JSON::Validator->new;
my $id = File::Spec->case_tolerant ? lc $spec : $spec->to_string;
note $spec->to_string;
ok eval { $jv->schema($file) }, 'loaded from file://' or diag $@;
isa_ok $jv->schema, 'JSON::Validator::Schema';
is $jv->schema->get('/title'), 'Example Schema', 'got example schema';
is $jv->schema->id, $id, 'schema id';
is_deeply [sort keys %{$jv->store->schemas}], [$jv->schema->id], 'schemas in store';
ok eval { $jv->schema($spec->to_string) }, 'loaded from file:// again' or diag $@;
is $jv->schema->id, $id, 'schema id again';
is_deeply [sort keys %{$jv->store->schemas}], [$jv->schema->id], 'schemas in store again';
eval { $jv->load_and_validate_schema('no-such-file.json') };
like $@, qr{Unable to load schema "no-such-file\.json"}, 'cannot load no-such-file.json';
eval { $jv->load_and_validate_schema('/no-such-file.json') };
like $@, qr{Unable to load schema "/no-such-file\.json"},
'avoid loading from app, when $ua->server->app is not present';
done_testing;
JSON-Validator-5.18/t/random-errors.t 0000644 0000765 0000024 00000002414 15210277065 017262 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
# Note that you might have to run this test many times before it fails:
# while TEST_RANDOM_ITERATIONS=10000 prove -l t/random-errors.t; do echo "---"; done
plan skip_all => 'TEST_RANDOM_ITERATIONS=10000' unless my $iterations = $ENV{TEST_RANDOM_ITERATIONS};
my $jv = JSON::Validator->new->schema({
items => {
properties => {
prop1 => {type => [qw(string null)]},
prop2 => {type => [qw(string null)], format => 'ipv4'},
prop3 => {type => [qw(string null)], format => 'ipv4'},
prop4 => {type => 'string', enum => [qw(foo bar)]},
prop5 => {type => [qw(string null)]},
prop6 => {type => 'string'},
prop7 => {type => 'string', enum => [qw(foo bar)]},
prop8 => {type => [qw(string null)], format => 'ipv4'},
prop9 => {type => [qw(string null)]},
},
type => 'object',
},
type => 'array',
});
my @errors;
for (1 .. $iterations) {
push @errors,
$jv->validate([{
prop1 => undef,
prop2 => undef,
prop3 => undef,
prop4 => 'foo',
prop5 => undef,
prop6 => 'foo',
prop7 => 'bar',
prop8 => undef,
prop9 => undef,
}]);
last if @errors;
}
ok !@errors, 'no random error' or diag @errors;
done_testing;
JSON-Validator-5.18/t/jv-const.t 0000644 0000765 0000024 00000004536 15210277065 016242 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $faithful = {type => 'object', properties => {constancy => {const => "as the northern star"}}};
my $ambitious = {type => 'object', properties => {constancy => {const => "there is a tide in the affairs of men"}}};
validate_ok {name => "Caesar", constancy => "as the northern star"}, $faithful;
validate_ok {name => "Brutus", constancy => "there is a tide in the affairs of men"}, $ambitious;
validate_ok {name => "Cassius", constancy => "Cassius from bondage will deliver Cassius"}, $faithful,
E('/constancy', q{Does not match const: "as the northern star".});
validate_ok({name => "Calpurnia", constancy => "Do not go forth today. Call it my fear That keeps you in the house"},
$ambitious, E('/constancy', q{Does not match const: "there is a tide in the affairs of men".}));
# Now oneOf should work right
# before the fix, this failed with: "All of the oneOf rules match."
# because "likes: chocolate" vs. "peanutbutter" wasn't being considered
my $schema = {
type => 'object',
properties => {
people => {
type => 'array',
items => {oneOf => [{'$ref' => '#/definitions/chocolate'}, {'$ref' => '#/definitions/peanutbutter'}]},
},
},
definitions => {
chocolate => {
type => 'object',
properties => {name => {type => 'string'}, age => {type => 'number'}, likes => {const => 'chocolate'}},
},
peanutbutter => {
type => 'object',
properties => {name => {type => 'string'}, age => {type => 'number'}, likes => {const => 'peanutbutter'}},
},
},
};
validate_ok {people => [{name => 'mr. chocolate fan', age => 42, likes => 'peanutbutter'}]}, $schema;
my $null_const = {const => undef};
validate_ok 'foo', $null_const, E('/', q{Does not match const: null.});
validate_ok undef, $null_const;
my $empty_const = {const => ''};
validate_ok 'foo', $empty_const, E('/', q{Does not match const: "".});
validate_ok '', $empty_const;
my $array_constant = {const => [1, 'a', undef]};
validate_ok [1, 'a', undef], $array_constant;
validate_ok [1, 'b', undef], $array_constant, E('/', q{Does not match const: [1,"a",null].});
validate_ok true, {const => true};
validate_ok false, {const => false};
validate_ok false, {const => true}, E('/', 'Does not match const: true.');
validate_ok true, {const => false}, E('/', 'Does not match const: false.');
done_testing;
JSON-Validator-5.18/t/spec/ 0000755 0000765 0000024 00000000000 15211411467 015231 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/with-unicode-multibyte.json 0000644 0000765 0000024 00000000226 15210277065 022542 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"bar": {
"enum": ["replacement�char"]
},
"foo": {
"enum": ["foo♫bar"]
}
}
}
JSON-Validator-5.18/t/spec/bundlecheck.json 0000644 0000765 0000024 00000002247 15210277065 020403 0 ustar jhthorsen staff {
"basePath": "/api",
"consumes": [
"application/json"
],
"host": "localhost:3000",
"info": {
"license": {
"name": "Apache License, Version 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"title": "t-app",
"version": "0.1.0"
},
"paths": {
"/t": {
"get": {
"operationId": "listT",
"responses": {
"200": {
"description": "Self sufficient",
"schema": {
"items": {
"type": "string"
},
"type": "array"
}
},
"default": {
"$ref": "#/responses/error"
}
},
"tags": [
"t"
],
"x-mojo-to": "Controller::OpenAPI::T#list"
}
}
},
"produces": [
"application/json"
],
"responses": {
"error": {
"description": "Self sufficient",
"schema": {
"additionalProperties": false,
"properties": {
"error": {
"type": "string"
}
},
"required": [
"error"
],
"type": "object"
}
}
},
"swagger": "2.0"
}
JSON-Validator-5.18/t/spec/v3-petstore.json 0000644 0000765 0000024 00000010666 15210277065 020333 0 ustar jhthorsen staff {
"openapi": "3.0.0",
"info": {
"license": {
"name": "MIT"
},
"title": "Swagger Petstore",
"version": "1.0.0"
},
"servers": [
{ "url": "http://petstore.swagger.io/v1" }
],
"paths": {
"/pets/{petId}": {
"get": {
"operationId": "showPetById",
"tags": [ "pets" ],
"summary": "Info for a specific pet",
"parameters": [
{ "description": "The id of the pet to retrieve", "in": "path", "name": "petId", "required": true, "schema": { "type": "string" } },
{ "description": "Indicates if the age is wanted in the response object", "in": "query", "name": "wantAge", "schema": { "type": "boolean" } }
],
"responses": {
"default": {
"description": "unexpected error",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/Error" } },
"application/xml": { "schema": { "$ref": "#/components/schemas/Error" } }
}
},
"200": {
"description": "Expected response to a valid request",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } },
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }
}
}
}
}
},
"/pets": {
"get": {
"operationId": "listPets",
"summary": "List all pets",
"tags": [ "pets" ],
"parameters": [
{"name": "limit", "in": "query", "description": "How many items to return at one time (max 100)", "required": false, "schema": { "type": "integer", "default": 20, "format": "int32" }}
],
"responses": {
"200": {
"description": "An paged array of pets",
"headers": {
"x-next": { "schema": { "type": "string" }, "description": "A link to the next page of responses"}
},
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/Pets" } },
"application/xml": { "schema": { "$ref": "#/components/schemas/Pets" } }
}
},
"201": {
"$ref": "#/components/responses/Pets"
},
"default": {
"description": "unexpected error",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/Error" } },
"application/xml": { "schema": { "$ref": "#/components/schemas/Error" } }
}
}
}
},
"post": {
"operationId": "createPets",
"summary": "Create a pet",
"tags": [ "pets" ],
"parameters": [
{ "description": "Turn on/off debug", "in": "cookie", "name": "debug", "schema": { "type": "integer", "enum": [0, 1] } }
],
"requestBody": {
"required": true,
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } },
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } }
}
},
"responses": {
"201": {
"description": "Null response",
"content": {
"*/*": { "schema": { "type": "string" } }
}
},
"default": {
"description": "unexpected error",
"content": {
"*/*": { "schema": { "$ref": "#/components/schemas/Error" } }
}
}
}
}
}
},
"components": {
"schemas": {
"Pets": {
"type": "array",
"items": { "$ref": "#/components/schemas/Pet" }
},
"Pet": {
"required": [ "id", "name" ],
"properties": {
"tag": { "type": "string" },
"id": { "type": "integer", "format": "int64" },
"name": { "type": "string" },
"age": { "type": "integer" }
}
},
"Error": {
"required": [ "code", "message" ],
"properties": {
"code": { "format": "int32", "type": "integer" },
"message": { "type": "string" }
}
}
},
"responses": {
"Pets": {
"description": "Reusable Pets response",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/Pets" } },
"application/xml": { "schema": { "$ref": "#/components/schemas/Pets" } }
}
}
}
}
}
JSON-Validator-5.18/t/spec/with-unicode-multibyte.yml 0000644 0000765 0000024 00000000151 15210277065 022367 0 ustar jhthorsen staff ---
properties:
foo:
enum:
- foo♫bar
bar:
enum:
- replacement�char
type: object
JSON-Validator-5.18/t/spec/test-definitions-key.json 0000644 0000765 0000024 00000000406 15210277065 022205 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"age": { "$ref": "../definitions/age.json#" },
"weight": { "$ref": "../definitions/weight.json" },
"height": { "$ref": "#/definitions/height" }
},
"definitions": {
"height": { "type": "integer" }
}
}
JSON-Validator-5.18/t/spec/bundle-no-leaking-filename.json 0000644 0000765 0000024 00000000135 15210277065 023177 0 ustar jhthorsen staff {
"type": "object",
"properties": { "$ref": "./with-deep-mixed-ref.json#/properties" }
}
JSON-Validator-5.18/t/spec/v3-default-response-extra.yaml 0000644 0000765 0000024 00000002205 15210277065 023046 0 ustar jhthorsen staff openapi: 3.0.0
info:
title: v3-default-response-extra
version: 0.0.1
components:
schemas:
base:
type: object
required: [status, reason]
properties:
status:
type: integer
reason:
type: string
not_found:
type: object
allOf:
- $ref: '#/components/schemas/base'
exception:
type: object
allOf:
- $ref: '#/components/schemas/base'
responses:
'404':
description: Custom 404
content:
application/json:
schema:
$ref: '#/components/schemas/not_found'
paths:
/item/{id}:
get:
summary: get a single item
description: get a single item from the database
x-mojo-name: item
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/base'
'404':
$ref: '#/components/responses/404'
'500':
description: Custom 500
content:
application/json:
schema:
$ref: '#/components/schemas/exception'
JSON-Validator-5.18/t/spec/v2-bundle.yaml 0000644 0000765 0000024 00000000166 15210277065 017721 0 ustar jhthorsen staff ---
swagger: "2.0"
info:
title: Bundled
version: "1.0"
basePath: /api
paths:
/user:
$ref: paths.yaml#/user
JSON-Validator-5.18/t/spec/with-deep-mixed-ref.json 0000644 0000765 0000024 00000000424 15210277065 021673 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"age": { "$ref": "../definitions/age.json#" },
"weight": { "$ref": "../definitions/weight.json" },
"height": { "$ref": "#/definitions/height" }
},
"definitions": {
"height": { "type": "integer", "minimum": 5 }
}
}
JSON-Validator-5.18/t/spec/person.json 0000644 0000765 0000024 00000000530 15210277065 017433 0 ustar jhthorsen staff {
"title": "Example Schema",
"type": "object",
"required": ["firstName", "lastName"],
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"age": { "$ref": "#/definitions/age" }
},
"definitions": {
"age": { "type": "integer", "minimum": 0, "description": "Age in years" }
}
}
JSON-Validator-5.18/t/spec/missing-ref.json 0000644 0000765 0000024 00000000150 15210277065 020346 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"missing": { "$ref": "../definitions/missing.json#" }
}
}
JSON-Validator-5.18/t/spec/remotes/ 0000755 0000765 0000024 00000000000 15211411467 016707 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/baseUriChangeFolder/ 0000755 0000765 0000024 00000000000 15211411467 022543 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/baseUriChangeFolder/folderInteger.json 0000644 0000765 0000024 00000000032 15210277065 026225 0 ustar jhthorsen staff {
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/baseUriChange/ 0000755 0000765 0000024 00000000000 15211411467 021407 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/baseUriChange/folderInteger.json 0000644 0000765 0000024 00000000032 15210277065 025071 0 ustar jhthorsen staff {
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/locationIndependentIdentifierDraft4.json 0000644 0000765 0000024 00000000260 15210277065 026641 0 ustar jhthorsen staff {
"definitions": {
"refToInteger": {
"$ref": "#foo"
},
"A": {
"id": "#foo",
"type": "integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/name.json 0000644 0000765 0000024 00000000404 15210277065 020523 0 ustar jhthorsen staff {
"definitions": {
"orNull": {
"anyOf": [
{
"type": "null"
},
{
"$ref": "#"
}
]
}
},
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/subSchemas-defs.json 0000644 0000765 0000024 00000000241 15210277065 022616 0 ustar jhthorsen staff {
"$defs": {
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/$defs/integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/ 0000755 0000765 0000024 00000000000 15211411467 020533 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2020-12/baseUriChangeFolder/ 0000755 0000765 0000024 00000000000 15211411467 024367 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2020-12/baseUriChangeFolder/folderInteger.json 0000644 0000765 0000024 00000000131 15210277065 030051 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/baseUriChange/ 0000755 0000765 0000024 00000000000 15211411467 023233 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2020-12/baseUriChange/folderInteger.json 0000644 0000765 0000024 00000000131 15210277065 026715 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/metaschema-no-validation.json 0000644 0000765 0000024 00000000737 15210277065 026311 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://localhost:1234/draft2020-12/metaschema-no-validation.json",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
"https://json-schema.org/draft/2020-12/vocab/core": true
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/2020-12/meta/applicator" },
{ "$ref": "https://json-schema.org/draft/2020-12/meta/core" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/format-assertion-true.json 0000644 0000765 0000024 00000000750 15210277065 025705 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft2020-12/format-assertion-true.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/format-assertion": true
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/2020-12/meta/core" },
{ "$ref": "https://json-schema.org/draft/2020-12/meta/format-assertion" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/subSchemas-defs.json 0000644 0000765 0000024 00000000340 15210277065 024442 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/$defs/integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/name-defs.json 0000644 0000765 0000024 00000000475 15210277065 023276 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"orNull": {
"anyOf": [
{
"type": "null"
},
{
"$ref": "#"
}
]
}
},
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/prefixItems.json 0000644 0000765 0000024 00000000273 15210277065 023732 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft2020-12/prefixItems.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"prefixItems": [
{"type": "string"}
]
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/subSchemas.json 0000644 0000765 0000024 00000000255 15210277065 023530 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/integer"
}
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/baseUriChangeFolderInSubschema/ 0000755 0000765 0000024 00000000000 15211411467 026511 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2020-12/baseUriChangeFolderInSubschema/folderInteger.json 0000644 0000765 0000024 00000000131 15210277065 032173 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/locationIndependentIdentifier.json 0000644 0000765 0000024 00000000355 15210277065 027425 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"refToInteger": {
"$ref": "#foo"
},
"A": {
"$anchor": "foo",
"type": "integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/format-assertion-false.json 0000644 0000765 0000024 00000000752 15210277065 026022 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft2020-12/format-assertion-false.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/format-assertion": false
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/2020-12/meta/core" },
{ "$ref": "https://json-schema.org/draft/2020-12/meta/format-assertion" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/extendible-dynamic-ref.json 0000644 0000765 0000024 00000001024 15210277065 025745 0 ustar jhthorsen staff {
"description": "extendible array",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://localhost:1234/draft2020-12/extendible-dynamic-ref.json",
"type": "object",
"properties": {
"elements": {
"type": "array",
"items": {
"$dynamicRef": "#elements"
}
}
},
"required": ["elements"],
"additionalProperties": false,
"$defs": {
"elements": {
"$dynamicAnchor": "elements"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/ref-and-defs.json 0000644 0000765 0000024 00000000453 15210277065 023666 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://localhost:1234/draft2020-12/ref-and-defs.json",
"$defs": {
"inner": {
"properties": {
"bar": { "type": "string" }
}
}
},
"$ref": "#/$defs/inner"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/nested/ 0000755 0000765 0000024 00000000000 15211411467 022015 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2020-12/nested/foo-ref-string.json 0000644 0000765 0000024 00000000232 15210277065 025551 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"foo": {"$ref": "string.json"}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/nested/string.json 0000644 0000765 0000024 00000000130 15210277065 024213 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/integer.json 0000644 0000765 0000024 00000000131 15210277065 023061 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2020-12/tree.json 0000644 0000765 0000024 00000000632 15210277065 022371 0 ustar jhthorsen staff {
"description": "tree schema, extensible",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://localhost:1234/draft2020-12/tree.json",
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"data": true,
"children": {
"type": "array",
"items": {
"$dynamicRef": "#node"
}
}
}
}
JSON-Validator-5.18/t/spec/remotes/folder/ 0000755 0000765 0000024 00000000000 15211411467 020162 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/folder/folderInteger.json 0000644 0000765 0000024 00000000002 15210277065 023641 0 ustar jhthorsen staff {} JSON-Validator-5.18/t/spec/remotes/draft7/ 0000755 0000765 0000024 00000000000 15211411467 020076 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft7/ignore-dependentRequired.json 0000644 0000765 0000024 00000000255 15210277065 025726 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft7/integer.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"dependentRequired": {
"foo": ["bar"]
}
} JSON-Validator-5.18/t/spec/remotes/name-defs.json 0000644 0000765 0000024 00000000376 15210277065 021452 0 ustar jhthorsen staff {
"$defs": {
"orNull": {
"anyOf": [
{
"type": "null"
},
{
"$ref": "#"
}
]
}
},
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/urn-ref-string.json 0000644 0000765 0000024 00000000204 15210277065 022463 0 ustar jhthorsen staff {
"$id": "urn:uuid:feebdaed-ffff-0000-ffff-0000deadbeef",
"$defs": {"bar": {"type": "string"}},
"$ref": "#/$defs/bar"
}
JSON-Validator-5.18/t/spec/remotes/nested-absolute-ref-to-string.json 0000644 0000765 0000024 00000000310 15210277065 025373 0 ustar jhthorsen staff {
"$defs": {
"bar": {
"$id": "http://localhost:1234/the-nested-id.json",
"type": "string"
}
},
"$ref": "http://localhost:1234/the-nested-id.json"
}
JSON-Validator-5.18/t/spec/remotes/subSchemas.json 0000644 0000765 0000024 00000000156 15210277065 021704 0 ustar jhthorsen staff {
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/integer"
}
}
JSON-Validator-5.18/t/spec/remotes/draft-next/ 0000755 0000765 0000024 00000000000 15211411467 020763 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft-next/baseUriChangeFolder/ 0000755 0000765 0000024 00000000000 15211411467 024617 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft-next/baseUriChangeFolder/folderInteger.json 0000644 0000765 0000024 00000000126 15210277065 030305 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/baseUriChange/ 0000755 0000765 0000024 00000000000 15211411467 023463 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft-next/baseUriChange/folderInteger.json 0000644 0000765 0000024 00000000126 15210277065 027151 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/metaschema-no-validation.json 0000644 0000765 0000024 00000000716 15210277065 026536 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"$id": "http://localhost:1234/draft-next/metaschema-no-validation.json",
"$vocabulary": {
"https://json-schema.org/draft/next/vocab/applicator": true,
"https://json-schema.org/draft/next/vocab/core": true
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/next/meta/applicator" },
{ "$ref": "https://json-schema.org/draft/next/meta/core" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft-next/format-assertion-true.json 0000644 0000765 0000024 00000000727 15210277065 026141 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft-next/format-assertion-true.json",
"$schema": "https://json-schema.org/draft/next/schema",
"$vocabulary": {
"https://json-schema.org/draft/next/vocab/core": true,
"https://json-schema.org/draft/next/vocab/format-assertion": true
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/next/meta/core" },
{ "$ref": "https://json-schema.org/draft/next/meta/format-assertion" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft-next/subSchemas-defs.json 0000644 0000765 0000024 00000000335 15210277065 024676 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"$defs": {
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/$defs/integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft-next/name-defs.json 0000644 0000765 0000024 00000000472 15210277065 023523 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"$defs": {
"orNull": {
"anyOf": [
{
"type": "null"
},
{
"$ref": "#"
}
]
}
},
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/subSchemas.json 0000644 0000765 0000024 00000000252 15210277065 023755 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/integer"
}
}
JSON-Validator-5.18/t/spec/remotes/draft-next/baseUriChangeFolderInSubschema/ 0000755 0000765 0000024 00000000000 15211411470 026733 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft-next/baseUriChangeFolderInSubschema/folderInteger.json 0000644 0000765 0000024 00000000126 15210277065 032427 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/locationIndependentIdentifier.json 0000644 0000765 0000024 00000000352 15210277065 027652 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"$defs": {
"refToInteger": {
"$ref": "#foo"
},
"A": {
"$anchor": "foo",
"type": "integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft-next/format-assertion-false.json 0000644 0000765 0000024 00000000731 15210277065 026247 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft-next/format-assertion-false.json",
"$schema": "https://json-schema.org/draft/next/schema",
"$vocabulary": {
"https://json-schema.org/draft/next/vocab/core": true,
"https://json-schema.org/draft/next/vocab/format-assertion": false
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/next/meta/core" },
{ "$ref": "https://json-schema.org/draft/next/meta/format-assertion" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft-next/extendible-dynamic-ref.json 0000644 0000765 0000024 00000001017 15210277065 026177 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"description": "extendible array",
"$id": "http://localhost:1234/draft-next/extendible-dynamic-ref.json",
"type": "object",
"properties": {
"elements": {
"type": "array",
"items": {
"$dynamicRef": "#elements"
}
}
},
"required": ["elements"],
"additionalProperties": false,
"$defs": {
"elements": {
"$dynamicAnchor": "elements"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft-next/ref-and-defs.json 0000644 0000765 0000024 00000000446 15210277065 024120 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"$id": "http://localhost:1234/draft-next/ref-and-defs.json",
"$defs": {
"inner": {
"properties": {
"bar": { "type": "string" }
}
}
},
"$ref": "#/$defs/inner"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/nested/ 0000755 0000765 0000024 00000000000 15211411467 022245 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft-next/nested/foo-ref-string.json 0000644 0000765 0000024 00000000227 15210277065 026005 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"type": "object",
"properties": {
"foo": {"$ref": "string.json"}
}
}
JSON-Validator-5.18/t/spec/remotes/draft-next/nested/string.json 0000644 0000765 0000024 00000000125 15210277065 024447 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/integer.json 0000644 0000765 0000024 00000000126 15210277065 023315 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft-next/tree.json 0000644 0000765 0000024 00000000625 15210277065 022623 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/next/schema",
"description": "tree schema, extensible",
"$id": "http://localhost:1234/draft-next/tree.json",
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"data": true,
"children": {
"type": "array",
"items": {
"$dynamicRef": "#node"
}
}
}
}
JSON-Validator-5.18/t/spec/remotes/different-id-ref-string.json 0000644 0000765 0000024 00000000204 15210277065 024217 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/real-id-ref-string.json",
"$defs": {"bar": {"type": "string"}},
"$ref": "#/$defs/bar"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/ 0000755 0000765 0000024 00000000000 15211411467 020551 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2019-09/baseUriChangeFolder/ 0000755 0000765 0000024 00000000000 15211411467 024405 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2019-09/baseUriChangeFolder/folderInteger.json 0000644 0000765 0000024 00000000131 15210277065 030067 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/baseUriChange/ 0000755 0000765 0000024 00000000000 15211411467 023251 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2019-09/baseUriChange/folderInteger.json 0000644 0000765 0000024 00000000131 15210277065 026733 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/metaschema-no-validation.json 0000644 0000765 0000024 00000000737 15210277065 026327 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "http://localhost:1234/draft2019-09/metaschema-no-validation.json",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/applicator": true,
"https://json-schema.org/draft/2019-09/vocab/core": true
},
"allOf": [
{ "$ref": "https://json-schema.org/draft/2019-09/meta/applicator" },
{ "$ref": "https://json-schema.org/draft/2019-09/meta/core" }
]
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/subSchemas-defs.json 0000644 0000765 0000024 00000000340 15210277065 024460 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/$defs/integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/name-defs.json 0000644 0000765 0000024 00000000475 15210277065 023314 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"orNull": {
"anyOf": [
{
"type": "null"
},
{
"$ref": "#"
}
]
}
},
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/subSchemas.json 0000644 0000765 0000024 00000000255 15210277065 023546 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/integer"
}
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/baseUriChangeFolderInSubschema/ 0000755 0000765 0000024 00000000000 15211411467 026527 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2019-09/baseUriChangeFolderInSubschema/folderInteger.json 0000644 0000765 0000024 00000000131 15210277065 032211 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/locationIndependentIdentifier.json 0000644 0000765 0000024 00000000355 15210277065 027443 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$defs": {
"refToInteger": {
"$ref": "#foo"
},
"A": {
"$anchor": "foo",
"type": "integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/ignore-prefixItems.json 0000644 0000765 0000024 00000000302 15210277065 025222 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft2019-09/ignore-prefixItems.json",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"prefixItems": [
{"type": "string"}
]
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/extendible-dynamic-ref.json 0000644 0000765 0000024 00000001024 15210277065 025763 0 ustar jhthorsen staff {
"description": "extendible array",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "http://localhost:1234/draft2019-09/extendible-dynamic-ref.json",
"type": "object",
"properties": {
"elements": {
"type": "array",
"items": {
"$dynamicRef": "#elements"
}
}
},
"required": ["elements"],
"additionalProperties": false,
"$defs": {
"elements": {
"$dynamicAnchor": "elements"
}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/ref-and-defs.json 0000644 0000765 0000024 00000000453 15210277065 023704 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "http://localhost:1234/draft2019-09/ref-and-defs.json",
"$defs": {
"inner": {
"properties": {
"bar": { "type": "string" }
}
}
},
"$ref": "#/$defs/inner"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/dependentRequired.json 0000644 0000765 0000024 00000000303 15210277065 025112 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/draft2019-09/dependentRequired.json",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"dependentRequired": {
"foo": ["bar"]
}
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/nested/ 0000755 0000765 0000024 00000000000 15211411467 022033 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/draft2019-09/nested/foo-ref-string.json 0000644 0000765 0000024 00000000232 15210277065 025567 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"foo": {"$ref": "string.json"}
}
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/nested/string.json 0000644 0000765 0000024 00000000130 15210277065 024231 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/integer.json 0000644 0000765 0000024 00000000131 15210277065 023077 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/draft2019-09/tree.json 0000644 0000765 0000024 00000000632 15210277065 022407 0 ustar jhthorsen staff {
"description": "tree schema, extensible",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "http://localhost:1234/draft2019-09/tree.json",
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"data": true,
"children": {
"type": "array",
"items": {
"$dynamicRef": "#node"
}
}
}
}
JSON-Validator-5.18/t/spec/remotes/baseUriChangeFolderInSubschema/ 0000755 0000765 0000024 00000000000 15211411467 024665 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/baseUriChangeFolderInSubschema/folderInteger.json 0000644 0000765 0000024 00000000032 15210277065 030347 0 ustar jhthorsen staff {
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/locationIndependentIdentifier.json 0000644 0000765 0000024 00000000256 15210277065 025601 0 ustar jhthorsen staff {
"$defs": {
"refToInteger": {
"$ref": "#foo"
},
"A": {
"$anchor": "foo",
"type": "integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/extendible-dynamic-ref.json 0000644 0000765 0000024 00000000710 15210277065 024122 0 ustar jhthorsen staff {
"description": "extendible array",
"$id": "http://localhost:1234/extendible-dynamic-ref.json",
"type": "object",
"properties": {
"elements": {
"type": "array",
"items": {
"$dynamicRef": "#elements"
}
}
},
"required": ["elements"],
"additionalProperties": false,
"$defs": {
"elements": {
"$dynamicAnchor": "elements"
}
}
}
JSON-Validator-5.18/t/spec/remotes/ref-and-defs.json 0000644 0000765 0000024 00000000337 15210277065 022043 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/ref-and-defs.json",
"$defs": {
"inner": {
"properties": {
"bar": { "type": "string" }
}
}
},
"$ref": "#/$defs/inner"
}
JSON-Validator-5.18/t/spec/remotes/nested/ 0000755 0000765 0000024 00000000000 15211411467 020171 5 ustar jhthorsen staff JSON-Validator-5.18/t/spec/remotes/nested/foo-ref-string.json 0000644 0000765 0000024 00000000133 15210277065 023725 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"foo": {"$ref": "string.json"}
}
}
JSON-Validator-5.18/t/spec/remotes/nested/string.json 0000644 0000765 0000024 00000000031 15210277065 022367 0 ustar jhthorsen staff {
"type": "string"
}
JSON-Validator-5.18/t/spec/remotes/ref-and-definitions.json 0000644 0000765 0000024 00000000403 15210277065 023427 0 ustar jhthorsen staff {
"$id": "http://localhost:1234/ref-and-definitions.json",
"definitions": {
"inner": {
"properties": {
"bar": { "type": "string" }
}
}
},
"allOf": [ { "$ref": "#/definitions/inner" } ]
}
JSON-Validator-5.18/t/spec/remotes/integer.json 0000644 0000765 0000024 00000000032 15210277065 021235 0 ustar jhthorsen staff {
"type": "integer"
}
JSON-Validator-5.18/t/spec/remotes/locationIndependentIdentifierPre2019.json 0000644 0000765 0000024 00000000261 15210277065 026560 0 ustar jhthorsen staff {
"definitions": {
"refToInteger": {
"$ref": "#foo"
},
"A": {
"$id": "#foo",
"type": "integer"
}
}
}
JSON-Validator-5.18/t/spec/remotes/tree.json 0000644 0000765 0000024 00000000516 15210277065 020546 0 ustar jhthorsen staff {
"description": "tree schema, extensible",
"$id": "http://localhost:1234/tree.json",
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"data": true,
"children": {
"type": "array",
"items": {
"$dynamicRef": "#node"
}
}
}
}
JSON-Validator-5.18/t/spec/with-relative-ref.json 0000644 0000765 0000024 00000000140 15210277065 021460 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"age": { "$ref": "../definitions/age.json#" }
}
}
JSON-Validator-5.18/t/spec/paths.yaml 0000644 0000765 0000024 00000000542 15210277065 017240 0 ustar jhthorsen staff ---
user:
get:
parameters:
- $ref: "#/parameters/id_ref"
responses:
200:
description: A user
schema:
$ref: "#/x-def/User"
parameters:
id_ref:
$ref: "#/parameters/id"
id:
in: path
name: id
required: true
type: string
x-def:
User:
properties:
name:
type: string
JSON-Validator-5.18/t/spec/space bundle.json 0000644 0000765 0000024 00000000146 15210277065 020455 0 ustar jhthorsen staff {
"type": "object",
"properties": {
"age": { "$ref": "../definitions/space age.json#" }
}
}
JSON-Validator-5.18/t/spec/more-bundle.yaml 0000644 0000765 0000024 00000003566 15210277065 020343 0 ustar jhthorsen staff ---
$schema: http://json-schema.org/draft-07/schema#
definitions:
ref1:
type: array
items:
$ref: '#/definitions/ref2'
ref2:
type: string
minLength: 1
ref3:
type: integer
dupe_name:
type: integer
i_have_nested_refs:
type: object
properties:
my_key1:
$ref: '#/definitions/ref1'
my_key2:
$ref: '#/definitions/ref1'
# actually a person, as in https://json-schema.org/understanding-json-schema/structuring.html
i_have_a_recursive_ref:
type: object
properties:
name:
type: string
children:
type: array
items:
$ref: '#/definitions/i_have_a_recursive_ref'
default: []
i_have_a_ref_to_another_file:
type: object
properties:
name:
$ref: more-bundle2.yaml#/definitions/my_name
address:
$ref: more-bundle2.yaml#/definitions/my_address
secrets:
$ref: '#/definitions/ref1'
i_am_a_ref:
$ref: '#/definitions/ref1'
i_am_a_ref_level_1:
$ref: '#/definitions/i_am_a_ref_level_2'
i_am_a_ref_level_2:
$ref: '#/definitions/ref3'
i_am_a_ref_to_another_file:
$ref: more-bundle2.yaml#/definitions/i_have_a_ref_to_the_first_filename
i_am_a_ref_with_the_same_name:
$ref: more-bundle2.yaml#/definitions/i_am_a_ref_with_the_same_name
i_have_refs_with_the_same_name:
type: object
properties:
me:
$ref: '#/definitions/i_am_a_ref_with_the_same_name'
i_contain_refs_to_same_named_definitions:
type: object
properties:
foo:
$ref: '#/definitions/dupe_name'
bar:
$ref: more-bundle2.yaml#/definitions/dupe_name
i_have_a_ref_with_the_same_name:
type: object
properties:
name:
type: string
children:
type: array
items:
$ref: more-bundle2.yaml#/definitions/i_have_a_ref_with_the_same_name
default: []
JSON-Validator-5.18/t/spec/more-bundle2.yaml 0000644 0000765 0000024 00000001114 15210277065 020410 0 ustar jhthorsen staff ---
$schema: http://json-schema.org/draft-07/schema#
definitions:
my_name:
type: string
minLength: 2
my_address:
type: object
properties:
street:
type: string
city:
# this is a local ref in a secondary file - resolution is extra tricky
$ref: '#/definitions/my_name'
dupe_name:
type: string
i_am_a_ref_with_the_same_name:
type: string
i_have_a_ref_to_the_first_filename:
type: object
properties:
gotcha:
$ref: more-bundle.yaml#/definitions/ref3
i_have_a_ref_with_the_same_name:
type: string
JSON-Validator-5.18/t/spec/v2-petstore.json 0000644 0000765 0000024 00000005505 15210277065 020326 0 ustar jhthorsen staff {
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"contact": {"name": "OAI", "url": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore.json"},
"license": {"name": "MIT"}
},
"host": "petstore.swagger.io",
"basePath": "/v1",
"schemes": ["http"],
"consumes": ["application/json"],
"produces": ["application/json"],
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"operationId": "listPets",
"tags": ["pets"],
"parameters": [
{"name": "limit", "in": "query", "default": 20, "description": "How many items to return at one time (max 100)", "required": false, "type": "integer", "format": "int32"}
],
"responses": {
"200": {
"description": "An paged array of pets",
"headers": {
"x-next": {"type": "string", "description": "A link to the next page of responses"}
},
"schema": {"$ref": "#/definitions/Pets"}
},
"default": {
"description": "unexpected error",
"schema": {"$ref": "#/definitions/Error"}
}
}
},
"post": {
"summary": "Create a pet",
"operationId": "createPets",
"tags": ["pets"],
"parameters": [
{"in": "body", "name": "body", "required": true, "schema": {"$ref" : "#/definitions/Pet"}}
],
"responses": {
"201": {
"description": "Null response"
},
"default": {
"description": "unexpected error",
"schema": {"$ref": "#/definitions/Error"}
}
}
}
},
"/pets/{petId}": {
"parameters": [
{"name": "petId", "in": "path", "required": true, "description": "The id of the pet to retrieve", "type": "string"}
],
"get": {
"summary": "Info for a specific pet",
"operationId": "showPetById",
"tags": ["pets"],
"responses": {
"200": {
"description": "Expected response to a valid request",
"schema": {"$ref": "#/definitions/Pets"}
},
"default": {
"description": "unexpected error",
"schema": {"$ref": "#/definitions/Error"}
}
}
}
}
},
"definitions": {
"Pet": {
"required": ["id", "name"],
"properties": {
"id": {"type": "integer", "format": "int64"},
"name": {"type": "string"},
"tag": {"type": "string"}
}
},
"Pets": {
"type": "array",
"items": {"$ref": "#/definitions/Pet"}
},
"Error": {
"required": ["code", "message"],
"properties": {
"code": {"type": "integer", "format": "int32"},
"message": {"type": "string"}
}
}
}
}
JSON-Validator-5.18/t/openapiv3-basic.t 0000644 0000765 0000024 00000021070 15211411261 017437 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv3;
use Mojo::File;
use Test::Deep;
use Test::More;
my $cwd = Mojo::File->new(__FILE__)->dirname;
my $schema = JSON::Validator::Schema::OpenAPIv3->new;
my ($body, $p, @errors);
subtest 'basic' => sub {
is $schema->specification, 'https://spec.openapis.org/oas/3.0/schema/2021-09-28', 'specification';
is_deeply $schema->coerce, {booleans => 1, numbers => 1, strings => 1}, 'default coercion';
$schema = JSON::Validator->new->schema($cwd->child(qw(spec v3-petstore.json)))->schema;
isa_ok $schema, 'JSON::Validator::Schema::OpenAPIv3';
@errors = @{JSON::Validator->new->schema({openapi => '3.0.0', paths => {}})->schema->errors};
is "@errors", '/info: Missing property.', 'invalid schema';
is_deeply(
$schema->routes->to_array,
[
{method => 'get', operation_id => 'showPetById', path => '/pets/{petId}'},
{method => 'get', operation_id => 'listPets', path => '/pets'},
{method => 'post', operation_id => 'createPets', path => '/pets'},
],
'routes'
);
};
subtest base_url => sub {
is $schema->base_url, 'http://petstore.swagger.io/v1', 'get';
is $schema->base_url('https://api.example.com:8080/api'), $schema, 'set url';
is $schema->get('/servers/0/url'), 'https://api.example.com:8080/api', 'servers changed';
is $schema->base_url(Mojo::URL->new('//api2.example.com')), $schema, 'set without scheme';
is $schema->get('/servers/0/url'), 'https://api2.example.com', 'servers changed';
is $schema->base_url(Mojo::URL->new('/v1')), $schema, 'set path';
is $schema->base_url->to_string, 'https://api2.example.com/v1', 'get';
};
subtest 'parameters_for_request' => sub {
is $schema->parameters_for_request([GET => '/pets/nope']), undef, 'no such path';
cmp_deeply $schema->parameters_for_request([GET => '/pets']), [superhashof({in => 'query', name => 'limit'})],
'parameters_for_request inside path';
cmp_deeply $schema->parameters_for_request([post => '/pets']),
[
superhashof({in => 'cookie', name => 'debug'}),
superhashof({in => 'body', name => 'body', accepts => [qw(application/json application/x-www-form-urlencoded)]})
],
'parameters_for_request for body';
cmp_deeply $schema->parameters_for_request([get => '/pets/{petId}']),
[superhashof({in => 'path', name => 'petId'}), superhashof({in => 'query', name => 'wantAge'})],
'parameters_for_request inside method';
};
subtest 'parameters_for_response' => sub {
is $schema->parameters_for_response([GET => '/pets/nope']), undef, 'no such path';
cmp_deeply $schema->parameters_for_response([GET => '/pets']),
[
superhashof({in => 'header', name => 'x-next'}),
superhashof({in => 'body', name => 'body', accepts => [qw(application/json application/xml)]}),
],
'parameters_for_request inside path and default response code';
cmp_deeply $schema->parameters_for_response([GET => '/pets', 404]),
[superhashof({in => 'body', name => 'body', accepts => [qw(application/json application/xml)]})],
'default response';
};
subtest 'validate_request' => sub {
$p = Mojo::Parameters->new('limit=10&foo=42');
@errors = $schema->validate_request([get => '/pets'], {query => $p->to_hash});
is "@errors", '', 'limit ok, even as string';
@errors = $schema->validate_request([get => '/pets'], {query => {limit => 'foo'}});
is "@errors", '/limit: Expected integer - got string.', 'limit failed';
$body = {exists => 0};
@errors = $schema->validate_request([POST => '/pets'], {body => \&body});
is "@errors", '/body: Missing property.', 'default content type, but missing body';
is_deeply $body, {exists => 0, in => 'body', name => 'body', valid => 0}, 'input was mutated';
$body = {exists => 1, value => {name => 'kitty'}};
@errors = $schema->validate_request([POST => '/pets'], {body => \&body});
is "@errors", '/body/id: Missing property.', 'missing id in body';
$body = {exists => 1, value => {id => 42, name => 'kitty'}};
@errors = $schema->validate_request([POST => '/pets'], {body => \&body});
is "@errors", '', 'valid request body';
is_deeply $body,
{
content_type => 'application/json',
exists => 1,
in => 'body',
name => 'body',
valid => 1,
value => $body->{value}
},
'input was mutated';
};
subtest 'validate_response' => sub {
$body = {exists => 1, value => {id => 42, name => 'kitty'}};
@errors = $schema->validate_response([POST => '/pets', 201], {});
is "@errors", '', 'valid response body 201';
$body = {exists => 1, value => {code => 42}};
@errors = $schema->validate_response([post => '/pets', 200], {body => \&body});
is "@errors", '/body/message: Missing property.', 'valid response body default';
};
subtest 'validate_response - accept' => sub {
$body = {accept => 'text/plain'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '/header/Accept: Expected application/json, application/xml - got text/plain.', 'invalid accept';
is_deeply $body, {accept => 'text/plain', content_type => '', in => 'body', name => 'body', valid => 0},
'failed to negotiate content type';
$body = {accept => 'application/*'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '', 'valid accept';
is_deeply $body,
{accept => 'application/*', content_type => 'application/json', in => 'body', name => 'body', valid => 1},
'negotiated content type';
};
subtest 'validate_response - resusable response' => sub {
$body = {accept => 'application/*'};
@errors = $schema->validate_response([get => '/pets', 201], {body => \&body});
is "@errors", '', 'valid accept';
is_deeply $body,
{accept => 'application/*', content_type => 'application/json', in => 'body', name => 'body', valid => 1},
'negotiated content type';
};
subtest 'validate_response - content_type' => sub {
$body = {content_type => 'text/plain'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '/body: Expected application/json, application/xml - got text/plain.', 'invalid content_type';
is_deeply $body, {content_type => 'text/plain', in => 'body', name => 'body', valid => 0},
'failed to negotiate content type';
$body = {content_type => 'application/json'};
@errors = $schema->validate_response([get => '/pets'], {body => \&body});
is "@errors", '', 'valid content_type';
is_deeply $body,
{content_type => 'application/json', content_type => 'application/json', in => 'body', name => 'body', valid => 1},
'negotiated content type';
};
subtest add_default_response => sub {
my $schema = JSON::Validator->new->schema($cwd->child(qw(spec v3-petstore.json)))->schema;
ok !$schema->get('/components/schemas/DefaultResponse'), 'default response missing';
ok !$schema->get([paths => '/petss', 'get', 'responses', '400']), 'default response missing for 400';
$schema->add_default_response;
ok $schema->get('/components/schemas/DefaultResponse'), 'default response added';
for my $status (400, 401, 404, 500, 501) {
ok $schema->get([paths => '/pets', 'get', 'responses', $status]), "default response for $status";
}
delete $schema->{errors};
is_deeply $schema->errors, [], 'errors';
};
subtest 'add_default_response do not overwrite $ref' => sub {
my $schema = JSON::Validator->new->schema($cwd->child(qw(spec v3-default-response-extra.yaml)))->schema;
$schema->add_default_response;
is $schema->get([qw(paths /item/{id} get summary)]), 'get a single item', 'summary';
is $schema->get([qw(paths /item/{id} get responses 200 content application/json schema type)]), 'object',
'responses 200';
is $schema->get([qw(paths /item/{id} get responses 404 description)]), 'Custom 404', 'responses 404';
is $schema->get([qw(paths /item/{id} get responses 500 description)]), 'Custom 500', 'responses 500';
};
subtest 'v3.1.x' => sub {
my $schema = JSON::Validator->new->schema({openapi => '3.1.0', paths => {}})->schema;
is $schema->specification, 'https://spec.openapis.org/oas/3.1/schema/2021-05-20', 'specification';
is join(', ', @{$schema->errors}), '/info: Missing property.', 'errors';
};
subtest 'coerce defaults' => sub {
my $schema = JSON::Validator->new->coerce('defaults')->schema($cwd->child(qw(spec v3-petstore.json)))->schema;
is_deeply $schema->errors, [], 'defaults turned off when validating the schema';
};
done_testing;
sub body {$body}
JSON-Validator-5.18/t/draft4.t 0000644 0000765 0000024 00000002167 15210277065 015661 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use JSON::Validator::Schema::Draft4;
t::Helper->schema(JSON::Validator::Schema::Draft4->new);
t::Helper->test(number => qw(basic maximum minimum));
t::Helper->test(array => qw(basic items additional_items min_max unique));
t::Helper->test(object => qw(basic properties));
t::Helper->test(object => qw(additional_properties pattern_properties min_max));
subtest 'exclusiveMaximum' => sub {
schema_validate_ok 2.4, {exclusiveMaximum => true, maximum => 2.4}, E('/', '2.4 >= maximum(2.4)');
};
subtest 'exclusiveMinimum' => sub {
schema_validate_ok 0, {exclusiveMaximum => true, maximum => 0}, E('/', '0 >= maximum(0)');
};
subtest 'bundle' => sub {
my $bundle = JSON::Validator::Schema::Draft4->new('data://main/spec.json')->bundle;
is $bundle->data->{properties}{name}{'$ref'}, '#/definitions/defs_json-name', 'bundle ref';
is $bundle->data->{'definitions'}{'defs_json-name'}{type}, 'string', 'bundled spec under definitions';
};
done_testing;
__DATA__
@@ spec.json
{"type":"object","properties":{"name":{"$ref":"data://main/defs.json#/name"}}}
@@ defs.json
{"name":{"type":"string"}}
JSON-Validator-5.18/t/issue-22-duplicate-error-messages.t 0000644 0000765 0000024 00000000754 15210277065 022752 0 ustar jhthorsen staff use lib '.';
use t::Helper;
# https://github.com/jhthorsen/json-validator/issues/22
validate_ok {foo => 'x'}, 'data://main/test.schema', E('/foo', 'Not in enum list: bar, baz.');
validate_ok {foo => 123}, 'data://main/test.schema', E('/foo', 'Expected string - got number.');
done_testing;
__DATA__
@@ test.schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "test",
"type": "object",
"properties": {
"foo": {"type": "string", "enum": ["bar", "baz"]}
}
}
JSON-Validator-5.18/t/id-keyword-draft7.t 0000644 0000765 0000024 00000002604 15210277065 017734 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::Mojo;
use Test::More;
my ($base_url, $jv, $t, @e);
use Mojolicious::Lite;
get '/person' => [format => ['json']] => 'person';
get '/invalid-relative' => [format => ['json']] => 'invalid-relative';
$t = Test::Mojo->new;
$jv = JSON::Validator->new(ua => $t->ua);
eval {
$t->get_ok('/person.json')->status_is(200);
$base_url = $t->tx->req->url->to_abs->path('/');
$jv->load_and_validate_schema("${base_url}person.json", {schema => 'http://json-schema.org/draft-07/schema#'});
};
ok !$@, "${base_url}schema.json" or diag $@;
isa_ok $jv->schema, 'JSON::Validator::Schema::Draft7';
is $jv->schema->id, 'http://example.com/person.json', 'schema id';
is $jv->schema->moniker, 'draft07', 'moniker';
is $jv->schema->specification, 'http://json-schema.org/draft-07/schema#', 'schema specification';
eval { $jv->load_and_validate_schema("${base_url}invalid-relative.json") };
like $@, qr{Relative URL not allowed}, 'Root id cannot be relative' or diag $@;
done_testing;
__DATA__
@@ invalid-relative.json.ep
{"$id": "whatever", "$schema": "http://json-schema.org/draft-07/schema#"}
@@ person.json.ep
{
"$id": "http://example.com/person.json",
"definitions": {
"Person": {
"type": "object",
"properties": {
"firstName": { "type": "string" }
}
}
}
}
JSON-Validator-5.18/t/validate-recursive.t 0000644 0000765 0000024 00000002326 15210277065 020270 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::Mojo;
use Test::More;
use Mojolicious::Lite;
post '/' => sub {
my $c = shift;
my @errors = JSON::Validator->new->schema('data://main/spec.json')->validate($c->req->json);
$c->render(status => @errors ? 400 : 200, json => \@errors);
};
my $t = Test::Mojo->new;
$t->post_ok('/', json => {})->status_is(400)->content_like(qr{/person});
$t->post_ok('/', json => {person => {name => 'superwoman'}})->status_is(200);
$t->post_ok('/', json => {person => {name => 'superwoman', children => [{name => 'batboy'}]}})->status_is(200);
$t->post_ok('/', json => {person => {name => 'superwoman', children => [{}]}})->status_is(400)
->json_is('/0/path' => '/person/children/0/name');
done_testing;
__DATA__
@@ spec.json
{
"type": "object",
"properties": {
"person": {
"$ref": "#/definitions/person"
}
},
"required": [
"person"
],
"definitions": {
"person": {
"type": "object",
"required": [ "name" ],
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/person"
}
}
}
}
}
}
JSON-Validator-5.18/t/newline-warnings.t 0000644 0000765 0000024 00000000464 15210277065 017762 0 ustar jhthorsen staff use Mojo::Base -strict;
use Test::More;
use JSON::Validator;
my @warnings;
$SIG{__WARN__} = sub { push @warnings, @_ };
JSON::Validator->new->schema(q!{ "type": "object" }!."\n");
ok(!@warnings, "no warning emitted when ->schema() method is passed a valid JSON schema ending in newline");
done_testing;
JSON-Validator-5.18/t/recursive_data_protection.t 0000644 0000765 0000024 00000002142 15210277065 021734 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use JSON::Validator::Schema;
use Mojo::Util 'monkey_patch';
use Scalar::Util qw(refaddr);
use Test::More;
my ($original_validate, %ref_counts) = (\&JSON::Validator::Schema::_validate);
monkey_patch 'JSON::Validator::Schema', _validate => sub {
my ($self, $data, $path, $schema) = @_;
$ref_counts{refaddr($data)}++ if ref $data;
goto &$original_validate;
};
for ([1, 1], [0, 3]) {
my ($enabled, $exp_ref_counts) = @$_;
my $object = {level1 => {level2 => {level3 => 'Test'}}};
my $data = [$object, $object, $object];
%ref_counts = ();
JSON::Validator->new->recursive_data_protection($enabled)->schema(schema())->validate($data);
is $ref_counts{refaddr($object->{level1}{level2})}, $exp_ref_counts, "recursive_data_protection($enabled)";
}
done_testing;
sub schema {
return {
type => 'array',
items => {
type => 'object',
properties => {
level1 => {
type => 'object',
properties => {level2 => {type => 'object', properties => {level3 => {type => 'string'}}}}
}
}
}
};
}
JSON-Validator-5.18/t/jv-anyof.t 0000644 0000765 0000024 00000005651 15210277065 016227 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema = {anyOf => [{type => "string", maxLength => 5}, {type => "number", minimum => 0}]};
validate_ok 'short', $schema;
validate_ok 'too long', $schema, E('/', '/anyOf/0 String is too long: 8/5.'),
E('/', '/anyOf/1 Expected number - got string.');
validate_ok 12, $schema;
validate_ok int(-1), $schema, E('/', '/anyOf/0 Expected string - got number.'), E('/', '/anyOf/1 -1 < minimum(0)');
validate_ok {}, $schema, E('/', '/anyOf Expected string/number - got object.');
# anyOf with explicit integer (where _guess_data_type returns 'number')
my $schemaB = {anyOf => [{type => 'integer'}, {minimum => 2}]};
validate_ok 1, $schemaB;
validate_ok(
{type => 'string'},
{
properties => {
type => {
anyOf => [
{'$ref' => '#/definitions/simpleTypes'},
{
type => 'array',
items => {'$ref' => '#/definitions/simpleTypes'},
minItems => 1,
uniqueItems => Mojo::JSON::true,
}
]
},
},
definitions => {simpleTypes => {enum => [qw(array boolean integer null number object string)]}}
}
);
validate_ok(
{age => 6},
{
'$schema' => 'http://json-schema.org/draft-04/schema#',
type => 'object',
title => 'test',
description => 'test',
properties => {age => {type => 'number', anyOf => [{multipleOf => 5}, {multipleOf => 3}]}}
}
);
validate_ok(
{c => 'c present, a/b is missing'},
{
type => 'object',
properties => {a => {type => 'number'}, b => {type => 'string'}},
anyOf => [{required => ['a']}, {required => ['b']}],
},
E('/a', '/anyOf/0 Missing property.'),
E('/b', '/anyOf/1 Missing property.'),
);
validate_ok 'hello', {type => ['integer', 'string'], enum => [123, 'HELLO']}, E('/', 'Not in enum list: 123, HELLO.');
validate_ok 'hello', {anyOf => [false, {type => ['integer', 'boolean']}]}, E('/', '/anyOf/0 Should not match.'),
E('/', '/anyOf/1 Expected integer/boolean - got string.');
validate_ok 'hello', {type => ['integer', 'boolean']}, E('/', 'Expected integer/boolean - got string.');
validate_ok 'hello', {anyOf => [{type => ['integer', 'boolean']}]},
E('/', '/anyOf/0 Expected integer/boolean - got string.');
validate_ok 'hello',
{anyOf => [{anyOf => [{type => 'boolean'}, {type => 'string', maxLength => 2}]}, {type => 'integer'}]},
E('/', '/anyOf/0/anyOf/0 Expected boolean - got string.'), E('/', '/anyOf/0/anyOf/1 String is too long: 5/2.'),
E('/', '/anyOf/1 Expected integer - got string.');
validate_ok {foo => 'not an arrayref'}, {type => ['object', 'boolean'], properties => {foo => {type => 'array'}}},
E('/foo', 'Expected array - got string.');
validate_ok {foo => 'not an arrayref'},
{anyOf => [{type => 'object', properties => {foo => {type => 'array'}}}, {type => 'boolean'}]},
E('/foo', '/anyOf/0 Expected array - got string.'), E('/', '/anyOf/1 Expected boolean - got object.');
done_testing;
JSON-Validator-5.18/t/load-yaml-pp.t 0000644 0000765 0000024 00000000526 15210277065 016766 0 ustar jhthorsen staff BEGIN {
unshift @INC, sub {
my $file = $_[1];
die "Skipping $file in this test" if $file =~ m!YAML\W+XS\.pm$!;
};
}
use Test::More;
plan skip_all => 'YAML::PP not available' unless eval 'require JSON::Validator;1';
ok $INC{'YAML/PP.pm'}, 'YAML::PP was loaded';
ok !$INC{'YAML/XS.pm'}, 'YAML::XS was not loaded';
done_testing;
JSON-Validator-5.18/t/issue-103-one-of.t 0000644 0000765 0000024 00000003515 15210277065 017305 0 ustar jhthorsen staff use lib '.';
use t::Helper;
validate_ok {who_id => 'WHO', expire => '2018-01-01', amount => 1000, desc => 'foo'}, 'data://main/example.json',
E('/sym', '/oneOf/0/allOf/0/allOf/0 Missing property.'), E('/template', '/oneOf/0/allOf/2 Missing property.'),
E('/sym', '/oneOf/1/allOf/0 Missing property.'), E('/', '/oneOf/2 Expected string - got object.');
validate_ok {sym => 'a', expire => 'b', amount => 1, desc => 'foo', who_id => 'c', template => 'd'},
'data://main/example.json', E('/', 'oneOf rules 0, 1 match.');
done_testing;
__DATA__
@@ example.json
{
"oneOf": [
{"$ref": "#/definitions/template_1"},
{"$ref": "#/definitions/bar_header"},
{"type": "string"}
],
"definitions": {
"hwho":{
"required": [ "who_id" ],
"properties": {
"who_id": { "type": "string" },
"sub_who_id": { "type": "string" }
}
},
"header": {
"required": [ "sym", "expire" ],
"properties": {
"sym": { "type": "string" },
"expire": { "type": "string" }
}
},
"foo_header": {
"allOf": [
{ "$ref": "#/definitions/header" },
{
"required": [ "amount", "desc" ],
"properties": {
"amount": { "type": "integer" },
"desc": { "enum": [ "foo" ] }
}
}
]
},
"template_1": {
"allOf": [
{ "$ref": "#/definitions/foo_header" },
{ "$ref": "#/definitions/hwho" },
{ "required": [ "template" ], "properties": { "template": { "type": "string" } } }
]
},
"bar_header" : {
"allOf": [
{ "$ref": "#/definitions/header" },
{
"required": [ "amount", "desc" ],
"properties": {
"amount": { "type": "integer" },
"desc": { "enum": [ "foo" ] }
}
}
]
}
}
}
JSON-Validator-5.18/t/openapiv3-file.t 0000644 0000765 0000024 00000007306 15211411261 017303 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv3;
use Mojo::File;
use Mojo::Upload;
use Test::Deep;
use Test::More;
my $cwd = Mojo::File->new(__FILE__)->dirname;
my $schema = JSON::Validator::Schema::OpenAPIv3->new;
my ($body, $p, @errors);
subtest 'basic' => sub {
is $schema->specification, 'https://spec.openapis.org/oas/3.0/schema/2021-09-28', 'specification';
is_deeply $schema->coerce, {booleans => 1, numbers => 1, strings => 1}, 'default coercion';
$schema = JSON::Validator->new->schema('data://main/spec.yaml')->schema;
isa_ok $schema, 'JSON::Validator::Schema::OpenAPIv3';
@errors = @{JSON::Validator->new->schema({openapi => '3.0.0', paths => {}})->schema->errors};
is "@errors", '/info: Missing property.', 'invalid schema';
is_deeply(
$schema->routes->to_array,
[
{method => 'post', operation_id => 'submit', path => '/submit'},
{method => 'post', operation_id => 'uploadFile', path => '/upload'},
],
'routes'
);
};
subtest 'validate_request - file string' => sub {
my $file = 'file contents\nmore content';
# Check required works
$body = {exists => 1,value => {image => $file } };
@errors = $schema->validate_request([post => '/upload'], {body => \&body});
is "@errors", "/body/file: Missing property.", 'detects missing file';
$body = {exists => 1,value => {file => $file } };
@errors = $schema->validate_request([post => '/upload'], {body => \&body});
is "@errors", "", 'valid file';
is_deeply $body, {
content_type => 'multipart/form-data',
exists => 1,
in => 'body',
name => 'body',
valid => 1,
value => {file => $file},
}, 'valid file';
};
subtest 'validate_request - file placeholder' => sub {
my $file = Mojo::Upload->new;
# Check required works
$body = {exists => 1,value => {image => $file } };
@errors = $schema->validate_request([post => '/upload'], {body => \&body});
is "@errors", "/body/file: Missing property.", 'detects missing file';
$body = {exists => 1,value => {file => $file } };
@errors = $schema->validate_request([post => '/upload'], {body => \&body});
is "@errors", "", 'valid file';
is_deeply $body, {
content_type => 'multipart/form-data',
exists => 1,
in => 'body',
name => 'body',
valid => 1,
value => {file => $file},
}, 'valid file';
};
subtest 'string in non-file string' => sub {
$body = {exists => 1,value => {name => 'some string' } };
@errors = $schema->validate_request([post => '/submit'], {body => \&body});
is "@errors", "", 'valid file';
is_deeply $body, {
content_type => 'multipart/form-data',
exists => 1,
value => {name => 'some string'},
in => 'body',
name => 'body',
valid => 1,
}, 'valid file';
};
subtest 'file placehodler in non-file string' => sub {
my $file = Mojo::Upload->new;
$body = {exists => 1,value => {name => $file } };
@errors = $schema->validate_request([post => '/submit'], {body => \&body});
is "@errors", "/body/name: Expected string - got file.", 'valid file';
};
done_testing;
sub body {$body}
__DATA__
@@ spec.yaml
openapi: 3.0.0
info:
title: Test body
version: 0.8
paths:
/submit:
post:
operationId: submit
requestBody:
content:
multipart/form-data:
schema:
type: object
required:
- name
properties:
name:
type: string
/upload:
post:
operationId: uploadFile
requestBody:
content:
multipart/form-data:
schema:
type: object
required:
- file
properties:
file:
type: string
format: binary
JSON-Validator-5.18/t/openapiv2-headers.t 0000644 0000765 0000024 00000006035 15210277065 020007 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::Headers;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my $headers = Mojo::Headers->new;
my $body = sub { +{exists => 1, value => {}} };
my @errors;
$headers->header('X-Number' => 'x')->header('X-String' => '123');
@errors = $schema->validate_request([get => '/test'], {header => $headers->to_hash(1)});
is "@errors", '/X-Number: Expected number - got string.', 'request header not a number';
$headers->header('X-Number' => '42');
@errors = $schema->validate_request([get => '/test'], {header => $headers->to_hash(1)});
is "@errors", '', 'request header is number';
$headers->header('X-Array' => '42');
@errors = $schema->validate_request([get => '/test'], {header => $headers->to_hash});
is ref $headers->to_hash->{'X-Array'}, '', 'request header is not an array';
is "@errors", '', 'request header is coerced into an array';
@errors = $schema->validate_response([get => '/test'], {body => $body, header => $headers->to_hash});
is "@errors", '', 'response header is coerced into an array';
$headers->add('X-Array' => '3.14');
@errors = $schema->validate_request([get => '/test'], {header => $headers->to_hash(1)});
is ref $headers->to_hash(1)->{'X-Array'}, 'ARRAY', 'header is an array';
is "@errors", '', 'request header as array is valid';
$headers->header('X-Bool' => '42');
@errors = $schema->validate_request([get => '/test'], {header => $headers->to_hash(1)});
is "@errors", '/X-Bool: Expected boolean - got string.', 'request header not a boolean';
@errors = $schema->validate_response([get => '/test'], {body => $body, header => $headers->to_hash(1)});
is "@errors", '/X-Bool: Expected boolean - got string.', 'response header not a boolean';
for my $str (qw(true false 1 0)) {
$headers->header('X-Bool' => $str);
@errors = $schema->validate_request([get => '/test'], {header => $headers->to_hash});
is "@errors", '', q(request header as boolean "$str");
@errors = $schema->validate_response([get => '/test'], {body => $body, header => $headers->to_hash(1)});
is "@errors", '', q(response header as boolean "$str");
}
done_testing;
__DATA__
@@ spec.json
{
"swagger": "2.0",
"info": {"version": "", "title": "Test headers"},
"basePath": "/api",
"paths": {
"/test": {
"get": {
"parameters": [
{"in": "header", "name": "X-Bool", "type": "boolean", "description": "desc..."},
{"in": "header", "name": "X-Number", "type": "number", "description": "desc..."},
{"in": "header", "name": "X-String", "type": "string", "description": "desc..."},
{"in": "header", "name": "X-Array", "items": {"type": "string"}, "type": "array", "description": "desc..."}
],
"responses": {
"200": {
"description": "this is required",
"headers": {
"X-Array": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"X-Bool": {"type": "boolean"}
},
"schema": {"type": "object"}
}
}
}
}
}
}
JSON-Validator-5.18/t/jv-boolean.t 0000644 0000765 0000024 00000006421 15210277065 016526 0 ustar jhthorsen staff use lib '.';
use t::Helper;
sub j { Mojo::JSON::decode_json(Mojo::JSON::encode_json($_[0])); }
my $schema = {type => 'object', properties => {v => {type => 'boolean'}}};
validate_ok {v => '0'}, $schema, E('/v', 'Expected boolean - got string.');
validate_ok {v => 'false'}, $schema, E('/v', 'Expected boolean - got string.');
validate_ok {v => 1}, $schema, E('/v', 'Expected boolean - got number.');
validate_ok {v => 0.5}, $schema, E('/v', 'Expected boolean - got number.');
validate_ok {v => Mojo::JSON->true}, $schema;
validate_ok {v => Mojo::JSON->false}, $schema;
validate_ok {v => true}, $schema;
validate_ok {v => 1000}, $schema, E('/v', 'Expected boolean - got number.');
validate_ok {v => 0.5}, $schema, E('/v', 'Expected boolean - got number.');
validate_ok {v => 'active'}, $schema, E('/v', 'Expected boolean - got string.');
validate_ok {v => bless({}, 'BoolTestOk')}, $schema;
validate_ok {v => bless({}, 'BoolTestFail')}, $schema, E('/v', 'Expected boolean - got BoolTestFail.');
validate_ok j(Mojo::JSON->false), {type => 'boolean'};
validate_ok j(Mojo::JSON->true), {type => 'boolean'};
validate_ok j('foo'), {type => 'boolean'}, E('/', 'Expected boolean - got string.');
validate_ok undef, {properties => {}}, E('/', 'Expected object - got null.');
note 'boolean const';
my $bool_constant_false = {type => 'boolean', const => false};
my $bool_constant_true = {type => 'boolean', const => true};
validate_ok false, $bool_constant_false;
validate_ok true, $bool_constant_false, E('/', q{Does not match const: false.});
validate_ok true, $bool_constant_true;
validate_ok false, $bool_constant_true, E('/', q{Does not match const: true.});
note 'boolean objects';
my $data = jv->store->get(jv->store->load(\"---\nv: true\n"));
isa_ok($data->{v}, 'JSON::PP::Boolean');
validate_ok $data, $schema;
SKIP: {
skip 'boolean not installed', 1 unless eval 'require boolean;1';
validate_ok {type => 'boolean'}, {type => 'object', properties => {type => {type => 'string'}}};
}
note 'coerce check data';
jv->coerce('bool');
coerce_ok({v => !!1}, $schema);
coerce_ok({v => !!0}, $schema);
coerce_ok({v => 0}, $schema);
coerce_ok({v => ''}, $schema);
coerce_ok({v => 'false'}, $schema);
coerce_ok({v => 'true'}, $schema);
coerce_ok({v => 1}, $schema);
coerce_ok({v => '1'}, $schema);
note 'coerce fail';
jv->coerce('booleans');
validate_ok {v => 0.5}, $schema, E('/v', 'Expected boolean - got number.');
validate_ok {v => -1}, $schema, E('/v', 'Expected boolean - got number.');
validate_ok {v => 'yessir'}, $schema, E('/v', 'Expected boolean - got string.');
validate_ok {v => 'nope'}, $schema, E('/v', 'Expected boolean - got string.');
note 'coerce const';
validate_ok 0, $bool_constant_false;
validate_ok 1, $bool_constant_false, E('/', q{Does not match const: false.});
validate_ok 1, $bool_constant_true;
validate_ok 0, $bool_constant_true, E('/', q{Does not match const: true.});
done_testing;
sub coerce_ok {
my ($data, $schema) = @_;
my $exp = {v => !$data->{v} || $data->{v} eq 'false' ? false : true};
validate_ok $data, $schema;
is_deeply $data, $exp, 'data was coerced correctly';
}
package BoolTestOk;
use overload '""' => sub {1};
package BoolTestFail;
use overload '""' => sub {2};
JSON-Validator-5.18/t/invalid-ref.t 0000644 0000765 0000024 00000001133 15210277065 016665 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::File 'path';
use Test::More;
eval { JSON::Validator->new->schema('data://main/spec.json') };
like $@, qr{Unable to resolve .*/definitions/Pet"}, 'missing definition';
my $workdir = path(__FILE__)->dirname;
eval { JSON::Validator->new->schema(path($workdir, 'spec', 'missing-ref.json')); };
ok $@, 'loading missing ref failed';
like $@, qr{Unable to load schema.*missing\.json}, 'error message' unless $^O eq 'MSWin32';
done_testing;
__DATA__
@@ spec.json
{
"schema": {
"type": "array",
"items": { "$ref": "#/definitions/Pet" }
}
}
JSON-Validator-5.18/t/issue-59-oneof-blessed-booleans.t 0000644 0000765 0000024 00000001202 15210277065 022370 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::JSON 'false';
use Test::More;
my $jv = JSON::Validator->new->schema('data://main/spec.json');
my @errors = $jv->validate({prop1 => false, prop2 => false});
is "@errors", '', 'oneof blessed booleans';
done_testing;
__DATA__
@@ spec.json
{
"type": "object",
"properties": {
"prop1": {
"$ref": "data://main/defs.json#/definitions/item"
},
"prop2": {
"$ref": "data://main/defs.json#/definitions/item"
}
}
}
@@ defs.json
{
"definitions": {
"item": {
"oneOf": [
{"type": "object"},
{"type": "boolean"}
]
}
}
}
JSON-Validator-5.18/t/load-json.t 0000644 0000765 0000024 00000002124 15210277065 016354 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::File 'path';
use Test::More;
my $file = path(path(__FILE__)->dirname, 'spec', 'person.json');
my $jv = JSON::Validator->new->schema($file);
my @errors = $jv->validate({firstName => 'yikes!'});
is int(@errors), 1, 'one error';
is $errors[0]->path, '/lastName', 'lastName';
is $errors[0]->message, 'Missing property.', 'required';
is_deeply $errors[0]->TO_JSON, {path => '/lastName', message => 'Missing property.'}, 'TO_JSON';
like $jv->schema->id, qr{^file:.*person\.json}, 'schema id';
is_deeply [sort keys %{$jv->store->schemas}], [$jv->schema->id], 'schemas in store'
or diag join ', ', sort keys %{$jv->store->schemas};
my $spec = path($file)->slurp;
$spec =~ s!"#!"person.json#! or die "Invalid spec: $spec";
path("$file.2")->spew($spec);
ok eval { JSON::Validator->new->schema("$file.2") }, 'test issue #1 where $ref could not point to a file' or diag $@;
unlink "$file.2";
note 'load from cache';
ok eval { JSON::Validator->new->schema('http://swagger.io/v2/schema.json') }, 'loaded from cache' or diag $@;
done_testing;
JSON-Validator-5.18/t/draft2019-09-acceptance.t 0000644 0000765 0000024 00000003125 15210277065 020416 0 ustar jhthorsen staff use lib '.';
use t::Helper;
$ENV{MOJO_LOG_LEVEL} //= 'fatal';
plan skip_all => 'TEST_ACCEPTANCE=1' unless $ENV{TEST_ACCEPTANCE};
delete $ENV{TEST_ACCEPTANCE} if $ENV{TEST_ACCEPTANCE} eq '1';
my @todo_tests;
push @todo_tests, ['', 'float and integers are equal up to 64-bit representation limits'];
push @todo_tests, ['defs.json', 'validate definition against metaschema'];
push @todo_tests, ['id.json', '$id inside an enum is not a real identifier'];
push @todo_tests, ['ref.json', 'ref creates new scope when adjacent to keywords'];
push @todo_tests, ['ref.json', 'refs with relative uris and defs'];
push @todo_tests, ['ref.json', 'relative refs with absolute uris and defs'];
push @todo_tests, ['ref.json', 'simple URN base URI with $ref via the URN'];
push @todo_tests, ['anchor.json', '$anchor inside an enum is not a real identifier'];
push @todo_tests, ['anchor.json', 'Location-independent identifier with base URI change in subschema'];
push @todo_tests, ['refRemote.json', 'Location-independent identifier in remote ref'];
push @todo_tests, ['refRemote.json', 'remote ref with ref to defs'];
push @todo_tests, ['recursiveRef.json'];
push @todo_tests, ['unevaluatedItems.json'];
push @todo_tests, ['unevaluatedProperties.json'];
push @todo_tests, ['unknownKeyword.json', '$id inside an unknown keyword is not a real identifier'];
push @todo_tests, ['vocabulary.json', 'schema that uses custom metaschema with with no validation vocabulary'];
t::Helper->acceptance('JSON::Validator::Schema::Draft201909', todo_tests => \@todo_tests);
done_testing;
JSON-Validator-5.18/t/remotes/ 0000755 0000765 0000024 00000000000 15211411467 015755 5 ustar jhthorsen staff JSON-Validator-5.18/t/remotes/folder/ 0000755 0000765 0000024 00000000000 15211411467 017230 5 ustar jhthorsen staff JSON-Validator-5.18/t/remotes/folder/folderInteger.json 0000644 0000765 0000024 00000000031 15210277065 022711 0 ustar jhthorsen staff {
"type": "integer"
} JSON-Validator-5.18/t/remotes/subSchemas.json 0000644 0000765 0000024 00000000157 15210277065 020753 0 ustar jhthorsen staff {
"integer": {
"type": "integer"
},
"refToInteger": {
"$ref": "#/integer"
}
}
JSON-Validator-5.18/t/remotes/integer.json 0000644 0000765 0000024 00000000031 15210277065 020302 0 ustar jhthorsen staff {
"type": "integer"
} JSON-Validator-5.18/t/relative-ref.t 0000644 0000765 0000024 00000000710 15210277065 017052 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use Mojo::File 'path';
my $file = path(path(__FILE__)->dirname, 'spec', 'with-relative-ref.json');
my $jv = jv->cache_paths([]);
validate_ok {age => -1}, $file, E('/age', '-1 < minimum(0)');
use Mojolicious::Lite;
push @{app->static->paths}, path(__FILE__)->dirname;
$jv->ua(app->ua);
validate_ok {age => -2}, app->ua->server->url->clone->path('/spec/with-relative-ref.json'),
E('/age', '-2 < minimum(0)');
done_testing;
JSON-Validator-5.18/t/coerce.t 0000644 0000765 0000024 00000002570 15210277065 015733 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::JSON 'to_json';
use Test::More;
my $jv = JSON::Validator->new;
my %coerce = (booleans => 1);
is_deeply($jv->coerce(%coerce)->coerce, {booleans => 1}, 'hash is accepted');
is_deeply($jv->coerce(\%coerce)->coerce, {booleans => 1}, 'hash reference is accepted');
note 'make sure input is coerced';
is_deeply($jv->coerce('booleans,numbers,strings')->coerce, {%coerce, numbers => 1, strings => 1}, '1 is accepted');
my @items = ([boolean => 'true'], [integer => '42'], [number => '4.2']);
for my $i (@items) {
for my $schema (schemas($i->[0])) {
my $x = $i->[1];
$jv->schema($schema)->validate($x);
is to_json($x), $i->[1], sprintf 'no quotes around %s %s', $i->[0], to_json($schema);
$x = {v => $i->[1]};
$jv->schema({type => 'object', properties => {v => $schema}})->validate($x);
is to_json($x->{v}), $i->[1], sprintf 'no quotes around %s %s', $i->[0], to_json($schema);
$x = [$i->[1]];
$jv->schema({type => 'array', items => $schema})->validate($x);
is to_json($x->[0]), $i->[1], sprintf 'no quotes around %s %s', $i->[0], to_json($schema);
}
}
done_testing;
sub schemas {
my $base = {type => shift};
return (
$base,
{type => ['array', $base->{type}]},
{allOf => [$base]},
{anyOf => [{type => 'array'}, $base]},
{oneOf => [$base, {type => 'array'}]},
);
}
JSON-Validator-5.18/t/jv-object.t 0000644 0000765 0000024 00000015053 15210277065 016356 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema;
subtest 'basic' => sub {
$schema = {type => 'object'};
validate_ok {mynumber => 1}, $schema;
validate_ok [1], $schema, E('/', 'Expected object - got array.');
};
subtest 'patternProperties' => sub {
$schema->{properties} = {
number => {type => 'number'},
street_name => {type => 'string'},
street_type => {type => 'string', enum => ['Street', 'Avenue', 'Boulevard']}
};
local $schema->{patternProperties} = {'^S_' => {type => 'string'}, '^I_' => {type => 'integer'}};
validate_ok {number => 1600, street_name => 'Pennsylvania', street_type => 'Avenue'}, $schema;
validate_ok {number => '1600', street_name => 'Pennsylvania', street_type => 'Avenue'}, $schema,
E('/number', 'Expected number - got string.');
validate_ok {number => 1600, street_name => 'Pennsylvania'}, $schema;
validate_ok {number => 1600, street_name => 'Pennsylvania', street_type => 'Avenue', direction => 'NW'}, $schema;
validate_ok {'S_25' => 'This is a string', 'I_0' => 42}, $schema;
validate_ok {'S_0' => 42}, $schema, E('/S_0', 'Expected string - got number.');
};
subtest 'additionalProperties' => sub {
local $schema->{additionalProperties} = 0;
validate_ok {number => 1600, street_name => 'Pennsylvania', street_type => 'Avenue', direction => 'NW',
foo => 'nope'}, $schema, E('/', 'Properties not allowed: direction, foo.');
$schema->{additionalProperties} = {type => 'string'};
validate_ok {number => 1600, street_name => 'Pennsylvania', street_type => 'Avenue', direction => 'NW'}, $schema;
};
subtest 'required' => sub {
local $schema->{required} = ['number', 'street_name'];
validate_ok {number => 1600, street_type => 'Avenue'}, $schema, E('/street_name', 'Missing property.');
};
subtest 'minProperties maxProperties' => sub {
$schema = {type => 'object', minProperties => 1};
validate_ok {}, $schema, E('/', 'Not enough properties: 0/1.');
$schema = {type => 'object', minProperties => 2, maxProperties => 3};
validate_ok {a => 1}, $schema, E('/', 'Not enough properties: 1/2.');
validate_ok {a => 1, b => 2}, $schema;
validate_ok {a => 1, b => 2, c => 3, d => 4}, $schema, E('/', 'Too many properties: 4/3.');
};
subtest 'dependencies' => sub {
$schema = {
type => 'object',
properties =>
{name => {type => 'string'}, credit_card => {type => 'number'}, billing_address => {type => 'string'}},
required => ['name'],
dependencies => {credit_card => ['billing_address']}
};
validate_ok {name => 'John Doe'}, $schema;
validate_ok {name => 'John Doe', billing_address => '123 Main St'}, $schema;
validate_ok {name => 'John Doe', credit_card => 5555555555555555}, $schema,
E('/billing_address', 'Missing property. Dependee: credit_card.');
$schema = {
type => 'object',
properties => {name => {type => 'string'}, credit_card => {type => 'number'}},
required => ['name'],
dependencies =>
{credit_card => {properties => {billing_address => {type => 'string'}}, required => ['billing_address']}},
};
validate_ok {name => 'John Doe'}, $schema;
validate_ok {name => 'John Doe', billing_address => '123 Main St'}, $schema;
validate_ok {name => 'John Doe', credit_card => 5555555555555555}, $schema,
E('/billing_address', 'Missing property.');
$schema = {dependencies => {bar => ['foo']}};
validate_ok {bar => 2}, $schema, E('/foo', 'Missing property. Dependee: bar.');
validate_ok {FOO => 1},
{
type => 'object',
propertyNames =>
{anyOf => [{type => 'string', enum => ['foo', 'bar', 'baz']}, {type => 'string', enum => ['hello']}]},
additionalProperties => {type => 'integer'},
},
E('/', '/propertyName/FOO /anyOf/0 Not in enum list: foo, bar, baz.'),
E('/', '/propertyName/FOO /anyOf/1 Not in enum list: hello.');
};
subtest 'patternProperties' => sub {
my $schema = {type => 'object', properties => {name => {type => 'string'}}};
validate_ok {}, $schema; # does not matter
ok !$schema->{patternProperties}, 'patternProperties was not added issue#47';
};
subtest 'propertyNames' => sub {
my $schema = {propertyNames => {minLength => 3, maxLength => 5}};
validate_ok {name => 'John', surname => 'Doe'}, $schema, E('/', '/propertyName/surname String is too long: 7/5.');
$schema->{propertyNames}{maxLength} = 7;
validate_ok {name => 'John', surname => 'Doe'}, $schema;
};
subtest 'TO_JSON' => sub {
my $obj = bless {age => 'not_a_string'}, 'main';
validate_ok $obj, {properties => {age => {type => 'integer'}}},
E('/age', 'Expected integer - got string.', 'age is not a string');
};
subtest 'const' => sub {
my $object_constant = {type => 'object', const => {a => 1}};
validate_ok {a => 1}, $object_constant;
validate_ok {b => 1}, $object_constant, E('/', q{Does not match const: {"a":1}.});
};
subtest 'boolean schemas' => sub {
validate_ok {foo => 'bar'}, {type => 'object', required => ['foo'], %$_}
for ({properties => {foo => {}}}, {additionalProperties => {}}, {patternProperties => {foo => {}}});
validate_ok {foo => 'bar'},
{definitions => {my_true_ref => {}}, type => 'object', required => ['foo'], %$_}
for (
{properties => {foo => {'$ref' => '#/definitions/my_true_ref'}}},
{additionalProperties => {'$ref' => '#/definitions/my_true_ref'}},
{patternProperties => {foo => {'$ref' => '#/definitions/my_true_ref'}}},
);
validate_ok {foo => 'bar'}, {type => 'object', required => ['foo'], %$_}
for ({properties => {foo => true}}, {additionalProperties => true}, {patternProperties => {foo => true}});
validate_ok {foo => 'bar'},
{definitions => {my_true_ref => true}, type => 'object', required => ['foo'], %$_}
for (
{properties => {foo => {'$ref' => '#/definitions/my_true_ref'}}},
{additionalProperties => {'$ref' => '#/definitions/my_true_ref'}},
{patternProperties => {foo => {'$ref' => '#/definitions/my_true_ref'}}},
);
validate_ok {foo => 'bar'}, {type => 'object', required => ['foo'], %$_}, E('/foo', 'Should not match.')
for ({properties => {foo => false}}, {patternProperties => {foo => false}});
validate_ok {foo => 'bar'}, {definitions => {my_false_ref => false}, type => 'object', required => ['foo'], %$_},
E('/foo', 'Should not match.')
for (
{properties => {foo => {'$ref' => '#/definitions/my_false_ref'}}},
{additionalProperties => {'$ref' => '#/definitions/my_false_ref'}},
{patternProperties => {foo => {'$ref' => '#/definitions/my_false_ref'}}},
);
};
done_testing;
sub TO_JSON { return {age => shift->{age}} }
JSON-Validator-5.18/t/draft7-acceptance.t 0000644 0000765 0000024 00000002700 15210277065 017741 0 ustar jhthorsen staff use lib '.';
use t::Helper;
$ENV{MOJO_LOG_LEVEL} //= 'fatal';
plan skip_all => 'TEST_ACCEPTANCE=1' unless $ENV{TEST_ACCEPTANCE};
delete $ENV{TEST_ACCEPTANCE} if $ENV{TEST_ACCEPTANCE} eq '1';
my @todo_tests;
push @todo_tests, ['id.json', 'id inside an enum is not a real identifier'];
push @todo_tests, ['const.json', 'float and integers are equal up to 64-bit representation limits'];
push @todo_tests, ['maxItems.json', 'maxItems validation with a decimal'];
push @todo_tests, ['maxLength.json', 'maxLength validation with a decimal'];
push @todo_tests, ['maxProperties.json', 'maxProperties validation with a decimal'];
push @todo_tests, ['minItems.json', 'minItems validation with a decimal'];
push @todo_tests, ['minLength.json', 'minLength validation with a decimal'];
push @todo_tests, ['minProperties.json', 'minProperties validation with a decimal'];
push @todo_tests, ['ref.json', '$ref prevents a sibling $id from changing the base uri'];
push @todo_tests, ['ref.json', 'simple URN base URI with $ref via the URN'];
push @todo_tests, ['refRemote.json', 'Location-independent identifier in remote ref'];
push @todo_tests, ['refRemote.json', 'remote ref with ref to definitions'];
push @todo_tests, ['unknownKeyword.json', '$id inside an unknown keyword is not a real identifier'];
t::Helper->acceptance('JSON::Validator::Schema::Draft7', todo_tests => \@todo_tests);
done_testing;
JSON-Validator-5.18/t/openapiv2-default-values.t 0000644 0000765 0000024 00000002205 15210277065 021310 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my @errors;
@errors = $schema->validate_request([get => '/pets/{id}'], {path => {id => 'a'}});
is "@errors", "/id: String is too short: 1/3.", 'invalid id';
@errors = $schema->validate_request([get => '/pets/{id}'], {path => {}});
is "@errors", "", 'default id';
my $id = {};
my %req = (path => sub {$id});
@errors = $schema->validate_request([get => '/pets/{id}'], \%req);
is_deeply $id, {exists => 1, in => 'path', name => 'id', valid => 1, value => 'foo'}, 'input was mutated';
is "@errors", "", 'default id';
done_testing;
__DATA__
@@ spec.json
{
"swagger": "2.0",
"info": {"version": "", "title": "Test default values"},
"basePath": "/api",
"paths": {
"/pets/{id}": {
"get": {
"parameters": [
{"name": "id", "in": "path", "type": "string", "default": "foo", "required": true, "minLength": 3}
],
"responses" : {
"200": {
"description": "pet response",
"schema": {"type": "object"}
}
}
}
}
}
}
JSON-Validator-5.18/t/jv-allof-and-not.t 0000644 0000765 0000024 00000001670 15210277065 017543 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $missing = E '/required', '/allOf/0 Missing property.';
my $schema = {type => 'object', allOf => [{required => ['required']}]};
my @tests = (
[{foo => 1, required => 2}, $schema],
[{foo => 2, forbidden => 3}, $schema, $missing],
[{foo => 3, forbidden => 3, required => 2}, $schema],
[{foo => 4}, $schema, $missing]
);
subtest 'property "required" must be present' => sub {
validate_ok @$_ for @tests;
};
subtest 'Property "forbidden" must not be present' => sub {
$schema->{not} = {required => ['forbidden']};
splice @{$tests[1]}, 2, 0, E '/', 'Should not match.';
$tests[2][2] = E '/', 'Should not match.';
validate_ok @$_ for @tests;
};
subtest 'Move "not" constraint to "allOf"' => sub {
push @{$schema->{allOf}}, {not => delete $schema->{not}};
$tests[1][2] = $tests[2][2] = E '/', '/allOf/1 Should not match.';
$tests[1][3] = $missing;
validate_ok @$_ for @tests;
};
done_testing;
JSON-Validator-5.18/t/load-yaml.t 0000644 0000765 0000024 00000001330 15210277065 016343 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $jv = JSON::Validator->new;
my @errors = $jv->schema('data://Some::Module/s_pec-/-ficaTion')->validate({firstName => 'yikes!'});
is int(@errors), 1, 'one error';
is $errors[0]->path, '/lastName', 'lastName';
is $errors[0]->message, 'Missing property.', 'required';
is_deeply $errors[0]->TO_JSON, {path => '/lastName', message => 'Missing property.'}, 'TO_JSON';
done_testing;
package Some::Module;
__DATA__
@@ s_pec-/-ficaTion
---
title: Example Schema
type: object
required:
- firstName
- lastName
properties:
firstName:
type: string
lastName:
type: string
age:
type: integer
minimum: 0
description: Age in years
JSON-Validator-5.18/t/openapiv2-bundle.t 0000644 0000765 0000024 00000002402 15210277065 017637 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv2;
use JSON::Validator::Util qw(str2data);
use Mojo::Loader qw(data_section);
use Test::Deep;
use Test::More;
my $cwd = Mojo::File->new(__FILE__)->dirname;
my $schema = JSON::Validator::Schema::OpenAPIv2->new($cwd->child(qw(spec v2-bundle.yaml)));
is_deeply $schema->errors, [], 'schema errors' or diag explain $schema->errors;
my $bundle = $schema->bundle;
is_deeply $bundle->errors, [], 'bundle errors' or diag explain $bundle->errors;
my $from_data = JSON::Validator::Schema::OpenAPIv2->new($bundle->data);
is_deeply $from_data->errors, [], 'from_data errors' or diag explain $from_data->errors;
is_deeply $from_data->data, str2data(data_section(qw(main exp.yaml))), 'from_data schema'
or diag explain $from_data->data;
done_testing;
__DATA__
@@ exp.yaml
---
swagger: "2.0"
info:
title: Bundled
version: "1.0"
basePath: /api
paths:
/user:
get:
parameters:
- $ref: "#/parameters/paths_yaml-parameters_id"
responses:
200:
description: A user
schema:
$ref: "#/x-def/User"
x-def:
User:
properties:
name:
type: string
parameters:
paths_yaml-parameters_id:
in: path
name: id
required: true
type: string
JSON-Validator-5.18/t/jv-allof.t 0000644 0000765 0000024 00000004122 15210277065 016200 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema = {allOf => [{type => 'string', maxLength => 5}, {type => 'string', minLength => 3}]};
validate_ok 'short', $schema;
validate_ok 12, $schema, E('/', '/allOf Expected string - got number.');
$schema = {allOf => [{type => 'string', maxLength => 7}, {type => 'string', maxLength => 5}]};
validate_ok 'superlong', $schema, E('/', '/allOf/0 String is too long: 9/7.'),
E('/', '/allOf/1 String is too long: 9/5.');
validate_ok 'toolong', $schema, E('/', '/allOf/1 String is too long: 7/5.');
$schema = {
allOf => [{type => 'string', maxLength => 5}, {type => 'string', minLength => 3}],
anyOf => [{pattern => '^[0-9]+$'}, {pattern => '^[a-z]+$'}],
oneOf => [{pattern => '^[0-9]+$'}, {pattern => '^[a-z]+$', maxLength => 4}],
};
validate_ok '123', $schema;
validate_ok 'aaaa', $schema;
validate_ok 'aaaaa', $schema, E('/', '/oneOf/0 String does not match ^[0-9]+$.'),
E('/', '/oneOf/1 String is too long: 5/4.');
validate_ok 'he110th3re', $schema, E('/', '/allOf/0 String is too long: 10/5.'),
E('/', '/anyOf/0 String does not match ^[0-9]+$.'), E('/', '/anyOf/1 String does not match ^[a-z]+$.'),
E('/', '/oneOf/0 String does not match ^[0-9]+$.'), E('/', '/oneOf/1 String is too long: 10/4.'),
E('/', '/oneOf/1 String does not match ^[a-z]+$.');
validate_ok 'hello', {type => ['integer', 'boolean']}, E('/', 'Expected integer/boolean - got string.');
validate_ok 'hello', {allOf => [{type => ['integer', 'boolean']}]},
E('/', '/allOf/0 Expected integer/boolean - got string.');
validate_ok 'hello',
{allOf => [{allOf => [{type => 'boolean'}, {type => 'string', maxLength => 2}]}, {type => 'integer'}]},
E('/', '/allOf/0/allOf/0 Expected boolean - got string.'), E('/', '/allOf/0/allOf/1 String is too long: 5/2.'),
E('/', '/allOf/1 Expected integer - got string.');
validate_ok {foo => 'not an arrayref'},
{allOf => [{type => 'object', properties => {foo => {type => 'array'}}}, {type => 'boolean'}]},
E('/foo', '/allOf/0 Expected array - got string.'), E('/', '/allOf/1 Expected boolean - got object.');
done_testing;
JSON-Validator-5.18/t/joi.t 0000644 0000765 0000024 00000012453 15210277065 015255 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use JSON::Validator::Joi 'joi';
use Storable 'dclone';
isa_ok +joi->validator, 'JSON::Validator::Schema';
is_deeply +joi->validator->coerce, {booleans => 1, numbers => 1, strings => 1}, 'default coercion';
is_deeply(
edj(joi->object->strict->props(
age => joi->integer->min(0)->max(200),
alphanum => joi->alphanum->length(12),
color => joi->string->min(2)->max(12)->pattern('^\w+$'),
date_time => joi->iso_date,
email => joi->string->email->required,
exists => joi->boolean,
lc => joi->lowercase,
name => joi->string->min(1),
pos => joi->positive,
token => joi->token,
uc => joi->uppercase,
uri => joi->uri,
)),
{
type => 'object',
required => ['email'],
properties => {
age => {type => 'integer', minimum => 0, maximum => 200},
alphanum => {type => 'string', minLength => 12, maxLength => 12, pattern => '^\w*$'},
color => {type => 'string', minLength => 2, maxLength => 12, pattern => '^\w+$'},
date_time => {type => 'string', format => 'date-time'},
email => {type => 'string', format => 'email'},
exists => {type => 'boolean'},
lc => {type => 'string', pattern => '^\p{Lowercase}*$'},
name => {type => 'string', minLength => 1},
pos => {type => 'number', minimum => 0},
token => {type => 'string', pattern => '^[a-zA-Z0-9_]+$'},
uc => {type => 'string', pattern => '^\p{Uppercase}*$'},
uri => {type => 'string', format => 'uri'},
},
additionalProperties => false
},
'generated correct object schema'
);
is_deeply(
edj(joi->array->min(0)->max(10)->strict->items(joi->integer->negative)),
{
additionalItems => false,
type => 'array',
minItems => 0,
maxItems => 10,
items => {type => 'integer', maximum => 0}
},
'generated correct array schema'
);
is_deeply(edj(joi->string->enum([qw(1.0 2.0)])), {type => 'string', enum => [qw(1.0 2.0)]}, 'enum for string');
is_deeply(edj(joi->integer->enum([qw(1 2 4 8 16)])), {type => 'integer', enum => [qw(1 2 4 8 16)]}, 'enum for integer');
joi_ok(
{age => 34, email => 'jhthorsen@cpan.org', name => 'Jan Henning Thorsen'},
joi->props(
age => joi->integer->min(0)->max(200),
email => joi->string->email->required,
name => joi->string->min(1),
),
);
joi_ok(
{age => -1, name => 'Jan Henning Thorsen'},
joi->props(
age => joi->integer->min(0)->max(200),
email => joi->string->email->required,
name => joi->string->min(1),
),
E('/age', '-1 < minimum(0)'),
E('/email', 'Missing property.'),
);
note 'test that compile and not compile generates same strict result';
my $strict_obj = joi->object->strict->props({ns => joi->string->required});
joi_ok({ns => 'plop', toto => 'plouf'}, $strict_obj, E('/', 'Properties not allowed: toto.'));
for my $item ($strict_obj->compile, $strict_obj) {
joi_ok([{ns => 'plop', toto => 'plouf'}], joi->array->strict->items($item), E('/0', 'Properties not allowed: toto.'),
);
}
note "can omit non-required objects containing required properties";
joi_ok({}, joi->object->props(a => joi->object->props(b => joi->integer->required)));
note "must include required objects containing required properties";
joi_ok(
{},
joi->object->props(a => joi->object->required->props(b => joi->integer->required)),
E('/a', 'Missing property.'),
);
eval { joi->number->extend(joi->integer) };
like $@, qr{Cannot extend joi 'number' by 'integer'}, 'need to extend same type';
test_extend(
joi->array->min(0)->max(10),
joi->array->min(5),
{type => 'array', minItems => 5, maxItems => 10},
'extended array',
);
test_extend(
joi->array->items([joi->integer]), joi->array->items([joi->number]),
{type => 'array', items => [{type => 'number'}]}, 'extended items in an array',
);
test_extend(
joi->integer->min(0)->max(10),
joi->integer->min(5),
{type => 'integer', minimum => 5, maximum => 10},
'extended integer',
'extended integer',
);
test_extend(
joi->object->props(x => joi->integer, y => joi->integer),
joi->object->props(x => joi->number),
{type => 'object', properties => {x => {type => 'number'}, y => {type => 'integer'}}},
'extended object',
);
is_deeply(
edj(joi->object->props(ip => joi->type([qw(string null)])->format('ip'), ns => joi->string)),
{type => 'object', properties => {ip => {format => 'ip', type => [qw(string null)]}, ns => {type => 'string'}}},
'null or string',
);
test_extend(
joi->object->props(a => joi->integer, b => joi->integer->required),
joi->object->props(b => joi->integer->required, x => joi->string->required, y => joi->string->required),
{
type => 'object',
required => bag(qw(b x y)),
properties =>
{a => {type => 'integer'}, b => {type => 'integer'}, x => {type => 'string'}, y => {type => 'string'}},
},
'extended object with required',
);
done_testing;
sub test_extend {
my ($joi, $by, $expected, $description) = @_;
my $joi_clone = dclone $joi;
my $by_clone = dclone $by;
cmp_deeply(edj($joi->extend($by)), $expected, $description);
cmp_deeply $joi, $joi_clone, "$description did not mutate \$joi";
cmp_deeply $by, $by_clone, "$description did not mutate \$by";
}
JSON-Validator-5.18/t/jv-enum.t 0000644 0000765 0000024 00000003425 15210277065 016054 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $male = {type => 'object', properties => {chromosomes => {enum => [[qw(X Y)], [qw(Y X)]]}}};
my $female = {type => 'object', properties => {chromosomes => {enum => [[qw(X X)]]}}};
validate_ok {name => "Kate", chromosomes => [qw(X X)]}, $female;
validate_ok {name => "Dave", chromosomes => [qw(X Y)]}, $male;
validate_ok {name => "Arnie", chromosomes => [qw(Y X)]}, $male;
validate_ok {name => "Kate", chromosomes => [qw(X X)]}, $male,
E('/chromosomes', 'Not in enum list: ["X","Y"], ["Y","X"].');
validate_ok {name => "Eddie", chromosomes => [qw(X YY )]}, $male,
E('/chromosomes', 'Not in enum list: ["X","Y"], ["Y","X"].');
validate_ok {name => "Steve", chromosomes => 'XY'}, $male, E('/chromosomes', 'Not in enum list: ["X","Y"], ["Y","X"].');
# https://github.com/jhthorsen/json-validator/issues/69
validate_ok(
{some_prop => ['foo']},
{
type => 'object',
required => ['some_prop'],
properties =>
{some_prop => {type => 'array', minItems => 1, maxItems => 1, items => [{type => 'string', enum => [qw(x y)]}]},},
},
E('/some_prop/0', 'Not in enum list: x, y.')
);
for my $v (undef, false, true) {
validate_ok(
{name => $v},
{
type => 'object',
required => ['name'],
properties => {name => {type => [qw(boolean null)], enum => [undef, false, true]}},
},
);
}
validate_ok(
{name => undef},
{
type => 'object',
required => ['name'],
properties => {name => {type => ['string'], enum => [qw(n yes true false)]}},
},
E('/name', 'Expected string - got null.'),
);
validate_ok(
{name => undef},
{type => 'object', required => ['name'], properties => {name => {enum => [qw(n yes true false)]}}},
E('/name', 'Not in enum list: n, yes, true, false.'),
);
done_testing;
JSON-Validator-5.18/t/Helper.pm 0000644 0000765 0000024 00000012540 15210277065 016061 0 ustar jhthorsen staff package t::Helper;
use Mojo::Base -base;
use JSON::Validator;
use Mojo::File;
use Mojo::JSON qw(decode_json encode_json);
use Mojo::Util qw(monkey_patch);
use Test::More;
$ENV{TEST_VALIDATOR_CLASS} = 'JSON::Validator';
sub acceptance {
my ($class, $schema_class, %acceptance_params) = @_;
Test::More::plan(skip_all => 'cpanm Test::JSON::Schema::Acceptance')
unless eval 'use Test::JSON::Schema::Acceptance 1.000 ();1';
Test::More::plan(skip_all => 'cpanm Test2::Tools::Compare') unless eval 'use Test2::Tools::Compare 0.0001 ();1';
Test::More::plan(skip_all => $@) unless eval "require $schema_class;1";
my $test = sub { +{file => $_[0], group_description => $_[1], test_description => $_[2]} };
my $ua = _acceptance_ua();
$acceptance_params{todo_tests} = [map { $test->(@$_) } @{$acceptance_params{todo_tests}}]
if $acceptance_params{todo_tests};
my $specification = $schema_class =~ m!::(\w+)$! ? lc $1 : 'unknown';
$specification = 'draft2019-09' if $specification eq 'draft201909';
Test::JSON::Schema::Acceptance->new(specification => $specification)->acceptance(
tests => $test->(split '/', $ENV{TEST_ACCEPTANCE} || ''),
%acceptance_params,
validate_data => sub {
my ($schema_p, $data_p) = map { Mojo::JSON::Pointer->new(shift @_) } qw(schema data);
my ($schema_d, $data_d) = map { decode_json(encode_json($_->data)) } $schema_p, $data_p;
my $schema = $schema_class->new($schema_d, ua => $ua);
return 0 if @{$schema->errors};
my @errors = $schema->validate($data_d);
# Doing internal tests on mutation, since I think Test::JSON::Schema::Acceptance is a bit too strict
Test2::Tools::Compare::is(encode_json($data_d), encode_json($data_p->data), 'data structure is the same');
Test2::Tools::Compare::is(encode_json($schema_d), encode_json($schema_p->data), 'schema structure is the same')
unless _skip_schema_is($schema_p);
return @errors ? 0 : 1;
},
);
}
sub edj {
return Mojo::JSON::decode_json(Mojo::JSON::encode_json(@_));
}
sub joi_ok {
my ($data, $joi, @expected) = @_;
my $description ||= @expected ? "errors: @expected" : "valid: " . encode_json($data);
my @errors = JSON::Validator::Joi->new($joi)->validate($data);
Test::More::is_deeply([map { $_->TO_JSON } sort { $a->path cmp $b->path } @errors],
[map { $_->TO_JSON } sort { $a->path cmp $b->path } @expected], $description)
or Test::More::diag(encode_json(\@errors));
}
sub jv { state $obj = $ENV{TEST_VALIDATOR_CLASS}->new }
sub schema { state $schema; $schema = $_[1] if $_[1]; $schema }
sub schema_validate_ok {
my ($data, $schema, @expected) = @_;
my $description = @expected ? "errors: @expected" : "valid: " . encode_json($data);
my @errors = t::Helper->schema->resolve($schema)->validate($data);
local $Test::Builder::Level = $Test::Builder::Level + 1;
Test::More::is_deeply([map { $_->TO_JSON } sort { $a->path cmp $b->path } @errors],
[map { $_->TO_JSON } sort { $a->path cmp $b->path } @expected], $description)
or Test::More::diag(encode_json(\@errors));
}
sub test {
my ($class, $category, @methods) = @_;
my $test_class = "t::test::$category";
eval "require $test_class;1" or die $@;
subtest "$category $_", sub { $test_class->$_ }
for @methods;
}
sub validate_ok {
my ($data, $schema, @expected) = @_;
my $description = @expected ? "errors: @expected" : "valid: " . encode_json($data);
my @errors = jv()->schema($schema)->validate($data);
local $Test::Builder::Level = $Test::Builder::Level + 1;
Test::More::is_deeply([map { $_->TO_JSON } sort { $a->path cmp $b->path } @errors],
[map { $_->TO_JSON } sort { $a->path cmp $b->path } @expected], $description)
or Test::More::diag(encode_json(\@errors));
}
sub import {
my $class = shift;
my $caller = caller;
eval "package $caller; use Test::Deep; use Test::More; 1" or die $@;
$_->import for qw(strict warnings);
feature->import(':5.10');
monkey_patch $caller => E => \&JSON::Validator::E;
monkey_patch $caller => done_testing => \&Test::More::done_testing;
monkey_patch $caller => edj => \&edj;
monkey_patch $caller => false => \&Mojo::JSON::false;
monkey_patch $caller => joi_ok => \&joi_ok;
monkey_patch $caller => jv => \&jv;
monkey_patch $caller => schema_validate_ok => \&schema_validate_ok;
monkey_patch $caller => true => \&Mojo::JSON::true;
monkey_patch $caller => validate_ok => \&validate_ok;
}
sub _acceptance_ua {
require Mojo::UserAgent;
require Mojolicious;
my $ua = Mojo::UserAgent->new;
my $app = Mojolicious->new;
$app->static->paths([Mojo::File->new(qw(t spec remotes))->to_string]);
$ua->server->app($app);
$ua->on(
$_ => sub {
my ($ua, $tx) = @_;
my $url = $tx->req->url;
$url->scheme(undef)->host(undef)->port(undef) if $url->host and $url->host eq 'localhost';
}
) for qw(prepare start);
return $ua;
}
sub _skip_schema_is {
my $p = shift;
my @paths = ('', '/properties/foo');
# The URL has been changed by _acceptance_ua()
return 1 if encode_json($p->data) =~ m!localhost:1234!;
# JSON::Validator always normalizes $ref with multiple keys
for my $path (@paths) {
my $ref = $p->get($path);
return 1 if ref $ref eq 'HASH' && $ref->{'$ref'} && 1 != keys %$ref;
}
return 0;
}
1;
JSON-Validator-5.18/t/draft6-acceptance.t 0000644 0000765 0000024 00000002700 15210277065 017740 0 ustar jhthorsen staff use lib '.';
use t::Helper;
$ENV{MOJO_LOG_LEVEL} //= 'fatal';
plan skip_all => 'TEST_ACCEPTANCE=1' unless $ENV{TEST_ACCEPTANCE};
delete $ENV{TEST_ACCEPTANCE} if $ENV{TEST_ACCEPTANCE} eq '1';
my @todo_tests;
push @todo_tests, ['const.json', 'float and integers are equal up to 64-bit representation limits'];
push @todo_tests, ['id.json', 'id inside an enum is not a real identifier'];
push @todo_tests, ['maxItems.json', 'maxItems validation with a decimal'];
push @todo_tests, ['maxLength.json', 'maxLength validation with a decimal'];
push @todo_tests, ['maxProperties.json', 'maxProperties validation with a decimal'];
push @todo_tests, ['minItems.json', 'minItems validation with a decimal'];
push @todo_tests, ['minLength.json', 'minLength validation with a decimal'];
push @todo_tests, ['minProperties.json', 'minProperties validation with a decimal'];
push @todo_tests, ['ref.json', '$ref prevents a sibling $id from changing the base uri'];
push @todo_tests, ['ref.json', 'simple URN base URI with $ref via the URN'];
push @todo_tests, ['refRemote.json', 'remote ref with ref to definitions'];
push @todo_tests, ['refRemote.json', 'Location-independent identifier in remote ref'];
push @todo_tests, ['unknownKeyword.json', '$id inside an unknown keyword is not a real identifier'];
t::Helper->acceptance('JSON::Validator::Schema::Draft6', todo_tests => \@todo_tests);
done_testing;
JSON-Validator-5.18/t/validate-id.t 0000644 0000765 0000024 00000000437 15210277065 016656 0 ustar jhthorsen staff use lib '.';
use t::Helper;
validate_ok {id => 1}, {type => 'object'};
validate_ok {id => 1, message => 'cannot exclude "id" #111'},
{type => 'object', additionalProperties => 0, properties => {message => {type => "string"}}},
E('/', 'Properties not allowed: id.');
done_testing;
JSON-Validator-5.18/t/util.t 0000644 0000765 0000024 00000010113 15210277065 015440 0 ustar jhthorsen staff use Mojo::Base -strict;
use Mojo::JSON 'false';
use Mojo::Util 'md5_sum';
use JSON::Validator;
use JSON::Validator::Util qw(E data_checksum data_type negotiate_content_type schema_type prefix_errors);
use JSON::Validator::Util qw(is_bool is_num is_type);
use Test::More;
my $e = E '/path/x', 'some error';
is "$e", '/path/x: some error', 'E';
is data_type('string'), 'string', 'data_type string';
is data_type(4.2), 'number', 'data_type number';
is data_type(42, [{type => 'integer'}]), 'integer', 'data_type integer';
is data_type([]), 'array', 'data_type array';
is data_type(bless {}, 'other'), 'other', 'data_type other';
is data_type(false), 'boolean', 'data_type boolean';
is data_type(undef), 'null', 'data_type null';
is data_type($e), 'JSON::Validator::Error', 'data_type JSON::Validator::Error';
my $v = JSON::Validator->new;
ok is_type($v, 'JSON::Validator'), 'is_type JSON::Validator';
ok is_type($v, 'Mojo::Base'), 'is_type Mojo::Base';
ok is_type($v, 'HASH'), 'is_type HASH';
ok is_type([], 'ARRAY'), 'is_type ARRAY';
ok is_type({}, 'HASH'), 'is_type HASH';
ok is_num(4.2), 'is_num 4.2';
ok is_num(42), 'is_num 42';
ok !is_num('2'), 'is_num 2';
ok is_bool(false), 'is_bool';
ok !is_bool(0), 'is_bool';
my $yikes = E {path => '/path/100/y', message => 'yikes'};
is_deeply(
[map {"$_"} prefix_errors 'allOf', [2, $e], [5, $yikes]],
['/path/x: /allOf/2 some error', '/path/100/y: /allOf/5 yikes'],
'prefix_errors',
);
is negotiate_content_type([]), '', 'accepts nothing';
is negotiate_content_type(['multipart/form-data'], 'multipart/form-data; boundary=mgkBX'), 'multipart/form-data',
'form-data boundary';
is negotiate_content_type(['application/json'], 'application/json;charset=UTF-8'), 'application/json', 'charset';
is negotiate_content_type(['application/json']), '', 'header missing';
is negotiate_content_type(['application/json', 'text/plain'], 'application/json'), 'application/json', 'exact match';
is negotiate_content_type(['application/json', 'text/*'], 'text/plain'), 'text/*', 'closest accept';
is negotiate_content_type(
['text/plain', 'application/xml'],
'text/html;text/plain;q=0.2,application/xml;q=0.9,*/*;q=0.8'
),
'application/xml', 'exact match with weight';
is negotiate_content_type(['application/xml'], 'application/json, text/plain, */*'), 'application/xml', 'star/star';
is schema_type({type => 'integer'}), 'integer', 'schema_type integer';
is schema_type({additionalProperties => {}}), 'object', 'schema_type object';
is schema_type({additionalProperties => {}}, {}), 'object', 'schema_type object';
is schema_type({additionalProperties => {}}, []), '', 'schema_type not object';
is schema_type({items => {}}), 'array', 'schema_type array';
is schema_type({items => {}}, {}), '', 'schema_type not array';
is schema_type({minLength => 4}), 'string', 'schema_type string';
is schema_type({multipleOf => 2}), 'number', 'schema_type number';
is schema_type({const => 42}), 'const', 'schema_type const';
is schema_type({cannot => 'guess'}), '', 'schema_type no idea';
subtest 'data_checksum with Sereal::Encoder' => sub {
plan skip_all => 'Sereal::Encoder 4.00+ not installed' unless JSON::Validator::Util->SEREAL_SUPPORT;
my $d_hash = {foo => {}, bar => {}};
my $d_hash2 = {bar => {}, foo => {}};
my $d_undef = {foo => undef};
my $d_obj = {foo => JSON::Validator::Error->new};
my $d_array = ['foo', 'bar'];
my $d_array2 = ['bar', 'foo'];
isnt data_checksum($d_array), data_checksum($d_array2), 'data_checksum array';
is data_checksum($d_hash), data_checksum($d_hash2), 'data_checksum hash field order';
isnt data_checksum($d_hash), data_checksum($d_undef), 'data_checksum hash not undef';
isnt data_checksum($d_hash), data_checksum($d_obj), 'data_checksum hash not object';
isnt data_checksum($d_obj), data_checksum($d_undef), 'data_checksum object not undef';
isnt data_checksum(3.14), md5_sum(3.15), 'data_checksum numeric';
is data_checksum(3.14), data_checksum('3.14'), 'data_checksum numeric like string';
};
done_testing;
JSON-Validator-5.18/t/issue-158-draf7-coerce-defaults.t 0000644 0000765 0000024 00000000521 15210277065 022174 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $validator = JSON::Validator->new(coerce => 'defaults');
eval {
$validator->load_and_validate_schema(
{'$schema' => 'http://json-schema.org/draft-07/schema#'},
{schema => 'http://json-schema.org/draft-07/schema#'},
);
};
ok !$@, "load_and_validate_schema draft-07 \$@=$@";
done_testing;
JSON-Validator-5.18/t/00-project.t 0000644 0000765 0000024 00000004013 15210277065 016350 0 ustar jhthorsen staff use strict;
use Test::More;
use File::Find;
plan skip_all => 'No such directory: .git' unless $ENV{TEST_ALL} or -d '.git';
plan skip_all => 'HARNESS_PERL_SWITCHES =~ /Devel::Cover/' if +($ENV{HARNESS_PERL_SWITCHES} || '') =~ /Devel::Cover/;
for (qw(
Test::CPAN::Changes::changes_file_ok+VERSION!4
Test::Pod::Coverage::pod_coverage_ok+VERSION!1
Test::Pod::pod_file_ok+VERSION!1
Test::Spelling::pod_file_spelling_ok+has_working_spellchecker!1
))
{
my ($fqn, $module, $sub, $check, $skip_n) = /^((.*)::(\w+))\+(\w+)!(\d+)$/;
next if eval "use $module;$module->$check";
no strict qw(refs);
*$fqn = sub {
SKIP: { skip "$sub(@_) ($module is required)", $skip_n }
};
}
my @files;
find({wanted => sub { /\.pm$/ and push @files, $File::Find::name }, no_chdir => 1}, -e 'blib' ? 'blib' : 'lib');
plan tests => @files * 4 + 4;
Test::Spelling::add_stopwords()
if Test::Spelling->can('has_working_spellchecker') && Test::Spelling->has_working_spellchecker;
for my $file (@files) {
my $module = $file;
$module =~ s,\.pm$,,;
$module =~ s,.*/?lib/,,;
$module =~ s,/,::,g;
ok eval "use $module; 1", "use $module" or diag $@;
Test::Pod::pod_file_ok($file);
Test::Pod::Coverage::pod_coverage_ok($module, {also_private => [qr/^[A-Z_]+$/]});
Test::Spelling::pod_file_spelling_ok($file);
}
Test::CPAN::Changes::changes_file_ok();
__DATA__
Aleksandr
Anwar
Aymeric
Barden
Bernhard
Berov
Böhmer
DT
Dagfinn
DefaultResponse
Etheridge
Fabrizio
Gennari
Goess
Graf
Hartmaier
Henning
Hradek
IRI
Ilmari
Ishigaki
JSONPatch
Jemmeson
Joi
Karelas
Kenichi
Kirill
Krasimir
Lari
Maijala
Mannsåker
Masse
Mattias
Matusov
Morrott
NID
NSS
OpenAPI
Orlenko
Päivärinta
Petstore
Rassadin
Renvoize
Riedel
Schemas
Schout
Stallard
Taskula
Thorsen
UUIDv
Znet
Zoffix
additionalItems
additionalProperties
allOf
alphanum
anyOf
basePath
bc
const
fff
formData
iban
ipv
joi
maxItems
maxLength
maxProperties
minItems
minLength
minProperties
multipleOf
nid
nss
oneOf
openapiv
schemas
str
ua
unevaluatedItems
unevaluatedProperties
uniqueItems
validator
validators
JSON-Validator-5.18/t/draft2019-09.t 0000644 0000765 0000024 00000004474 15210277065 016342 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use JSON::Validator::Schema::Draft201909;
my $schema = JSON::Validator::Schema::Draft201909->new;
t::Helper->schema($schema);
subtest 'formats' => sub {
ok $schema->formats->{duration}, 'duration';
ok $schema->formats->{uuid}, 'uuid';
};
t::Helper->test(number => qw(basic maximum minimum));
t::Helper->test(array => qw(basic items additional_items contains min_max min_max_contains));
t::Helper->test(array => qw(unique unevaluated_items));
t::Helper->test(object => qw(basic properties));
t::Helper->test(object => qw(additional_properties pattern_properties min_max names));
t::Helper->test(object => qw(dependent_required dependent_schemas unevaluated_properties));
subtest 'anchor' => sub {
$schema->resolve({'$ref' => '#foo', '$defs' => {'A' => {'$anchor' => 'foo', 'type' => 'integer'}}});
is $schema->get('/type'), 'integer', 'foo anchor type';
};
subtest 'recursiveRef, without recursiveAnchor' => sub {
my $jv = JSON::Validator->new->schema('data://main/tree.json');
$jv->schema('data://main/recursiveRef.json');
isa_ok $jv->schema, 'JSON::Validator::Schema::Draft201909';
is $jv->schema->get('/type'), 'object', 'recursiveRef type';
is $jv->schema->get('/properties/data'), true, 'recursiveRef properties data';
is $jv->schema->get('/properties/children/items/type'), 'object', 'recursiveRef properties data items';
is $jv->schema->get('/properties/children/items/properties/children/items/type'), 'object', 'recursive';
is_deeply [sort keys %{$jv->store->schemas}],
[qw(data://main/recursiveRef.json data://main/tree.json urn:x-test:recursiveRef urn:x-test:tree)],
'schemas in the store';
};
subtest 'test caching' => sub {
no warnings 'redefine';
local *JSON::Validator::_load_from_data = sub { die 'not cached' };
ok eval { JSON::Validator->new->schema('data://main/tree.json') }, 'cached' or diag $@;
};
done_testing;
__DATA__
@@ tree.json
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "urn:x-test:tree",
"type": "object",
"properties": {
"data": true,
"children": {
"type": "array",
"items": {"$recursiveRef": "#"}
}
}
}
@@ recursiveRef.json
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "urn:x-test:recursiveRef",
"$ref": "urn:x-test:tree"
}
JSON-Validator-5.18/t/openapiv3-coerce-array.t 0000644 0000765 0000024 00000004444 15210277065 020753 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/openapi.yaml')->schema;
my ($body, $query, @errors);
subtest 'number to array' => sub {
$body = {exists => 1, value => {id => 42}};
@errors = $schema->validate_request([post => '/test'], {body => \&body});
is "@errors", "", "valid";
};
subtest 'string to array' => sub {
$body = {exists => 1, value => {id => '42'}};
@errors = $schema->validate_request([post => '/test'], {body => \&body});
is "@errors", "", "valid";
};
subtest 'already an array' => sub {
$body = {exists => 1, value => {id => [42, '43']}};
@errors = $schema->validate_request([post => '/test'], {body => \&body});
is "@errors", "", "valid";
};
subtest 'array coercion with multipart/form-data boundary' => sub {
$body = {exists => 1, value => {id => 42}, content_type => 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'};
@errors = $schema->validate_request([post => '/test'], {body => \&body});
is "@errors", "", "valid with boundary parameter";
};
subtest 'parameter array schema is $ref' => sub {
$query = {exists => 1, value => [42, 43]};
@errors = $schema->validate_request([get => '/test'], {query => \&query});
is "@errors", "", "valid";
};
done_testing;
sub body {$body}
sub query {$query}
__DATA__
@@ openapi.yaml
---
openapi: 3.0.0
info:
title: Upload test
version: 1.0.0
servers:
- url: http://example.com/api
paths:
/test:
post:
operationId: testPost
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
properties:
id:
type: array
items:
type: integer
multipart/form-data:
schema:
properties:
id:
type: array
items:
type: integer
responses:
200:
description: OK
get:
parameters:
- name: id
in: query
required: true
schema:
$ref: '#/components/schemas/IntArray'
responses:
200:
description: OK
components:
schemas:
IntArray:
type: array
items:
type: integer
JSON-Validator-5.18/t/issue-42-cache-control.t 0000644 0000765 0000024 00000002076 15210277065 020566 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Mojo::File 'tempdir';
use Test::More;
plan skip_all => 'TEST_ONLINE=1' unless $ENV{TEST_ONLINE};
$ENV{JSON_VALIDATOR_CACHE_PATH} = '/tmp/whatever';
my $jv = JSON::Validator->new;
my @old_files = get_cached_files($jv);
is $jv->cache_paths->[0], '/tmp/whatever', 'back compat env';
shift @{$jv->cache_paths};
my $spec_url = 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v2.0/schema.json';
$jv->schema($spec_url);
my @new_files = get_cached_files($jv);
ok @old_files == @new_files, 'remote file not cached in default cache dir';
my $tempdir = tempdir;
$ENV{JSON_VALIDATOR_CACHE_PATH} = join ':', $tempdir->dirname, '/tmp/whatever';
$jv = JSON::Validator->new;
is $jv->cache_paths->[0], $tempdir->dirname, 'env';
$jv->schema($spec_url);
@new_files = get_cached_files($jv);
ok @new_files > @old_files, 'remote file cached when cache_paths not the default' or diag join "\n", @new_files;
done_testing;
sub get_cached_files {
my ($jv) = @_;
return sort map { glob "$_/*" } @{$jv->cache_paths};
}
JSON-Validator-5.18/t/more-bundle.t 0000644 0000765 0000024 00000016651 15210277065 016711 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::Deep;
use Test::More;
my $draft7_validator = JSON::Validator->new;
subtest 'setup draft7_validator' => sub {
$draft7_validator->schema('http://json-schema.org/draft-07/schema#');
isa_ok $draft7_validator->schema, 'JSON::Validator::Schema::Draft7';
is $draft7_validator->schema->id, 'http://json-schema.org/draft-07/schema#', 'draft7_validator schema id';
is $draft7_validator->schema->specification, $draft7_validator->schema->id, 'draft7_validator schema specification';
};
my $bundler_validator = JSON::Validator->new;
subtest 'setup bundler_validator' => sub {
$bundler_validator->load_and_validate_schema('t/spec/more-bundle.yaml');
isa_ok $bundler_validator->schema, 'JSON::Validator::Schema::Draft7';
like $bundler_validator->schema->id, qr{more-bundle\.yaml$}, 'bundler_validator schema id';
is $bundler_validator->schema->specification, 'http://json-schema.org/draft-07/schema#',
'bundler_validator schema specification';
};
bundle_test(
'find and resolve nested $refs; main schema is at the top level',
'i_have_nested_refs',
{
definitions => {
ref1 => {type => 'array', items => {'$ref' => '#/definitions/ref2'}},
ref2 => {type => 'string', minLength => 1},
},
# begin i_have_nested_refs definition
type => 'object',
properties => {my_key1 => {'$ref' => '#/definitions/ref1'}, my_key2 => {'$ref' => '#/definitions/ref1'}},
},
);
bundle_test(
'find and resolve recursive $refs',
'i_have_a_recursive_ref',
{
definitions => {
i_have_a_recursive_ref => {
type => 'object',
properties => {
name => {type => 'string'},
children => {type => 'array', items => {'$ref' => '#/definitions/i_have_a_recursive_ref'}, default => []},
},
},
},
# begin i_have_a_recursive_ref definition
# it is duplicated with the above, but there is no other way,
# because $ref cannot be combined with other sibling keys
type => 'object',
properties => {
name => {type => 'string'},
children => {type => 'array', items => {'$ref' => '#/definitions/i_have_a_recursive_ref'}, default => []},
},
},
);
bundle_test(
'find and resolve references to other local files',
'i_have_a_ref_to_another_file',
{
definitions => {
'more-bundle2_yaml-definitions_my_name' => {type => 'string', minLength => 2},
'more-bundle2_yaml-definitions_my_address' => {
type => 'object',
properties =>
{street => {type => 'string'}, city => {'$ref' => '#/definitions/more-bundle2_yaml-definitions_my_name'}}
},
ref1 => {type => 'array', items => {'$ref' => '#/definitions/ref2'}},
ref2 => {type => 'string', minLength => 1},
},
# begin i_have_a_ref_to_another_file definition
type => 'object',
properties => {
# these ref targets are rewritten
name => {'$ref' => '#/definitions/more-bundle2_yaml-definitions_my_name'},
address => {'$ref' => '#/definitions/more-bundle2_yaml-definitions_my_address'},
secrets => {'$ref' => '#/definitions/ref1'},
},
},
);
bundle_test(
'find and resolve references where the definition itself is a ref',
'i_am_a_ref',
{
definitions => {ref2 => {type => 'string', minLength => 1}},
# begin i_am_a_ref definition - which is actually ref1
type => 'array',
items => {'$ref' => '#/definitions/ref2'},
},
);
bundle_test(
'find and resolve references where the definition itself is a ref, multiple times over',
'i_am_a_ref_level_1',
{
# begin i_am_a_ref definition - which is actually (eventually) ref3
type => 'integer',
},
);
bundle_test(
'$refs which are simply $refs themselves are traversed automatically during resolution',
'i_have_refs_with_the_same_name',
{
definitions => {
i_am_a_ref_with_the_same_name =>
{'$ref' => '#/definitions/more-bundle2_yaml-definitions_i_am_a_ref_with_the_same_name'},
'more-bundle2_yaml-definitions_i_am_a_ref_with_the_same_name' => {type => 'string'},
},
# begin i_have_a_ref_with_the_same_name definition
type => 'object',
properties => {me => {'$ref' => '#/definitions/i_am_a_ref_with_the_same_name'}},
},
);
bundle_test(
'$refs which are simply $refs themselves are traversed automatically during resolution, at the top level too',
'i_am_a_ref_with_the_same_name',
{
# begin i_am_a_ref_with_the_same_name definition
# - pulled from secondary file
type => 'string',
},
);
bundle_test(
'when encountering references that have the same root name, one is renamed',
'i_contain_refs_to_same_named_definitions',
{
definitions => code(sub {
my $got = shift;
return (0, 'expected hash with 2 keys') unless ref($got) eq 'HASH' and keys %$got == 2;
return (0, 'missing "dupe_name" key') if not exists $got->{dupe_name};
# we don't know which ref will keep its name and which will be renamed
my ($other_key) = grep $_ ne 'dupe_name', keys %$got;
return 1
if (eq_deeply($got->{dupe_name}, {type => 'integer'})
and eq_deeply($got->{$other_key}, {type => 'string'})
and $other_key =~ qr/\bmore-bundle2_yaml-definitions_dupe_name$/);
return (0, 'uh oh, got: ' . (Test::More::explain($got))[0]);
}),
# begin i_contain_refs_to_same_named_definitions definition
type => 'object',
properties => {
foo => {'$ref' => '#/definitions/dupe_name'},
bar => {'$ref' => '#/definitions/more-bundle2_yaml-definitions_dupe_name'},
},
},
);
bundle_test(
'we can handle pulling in references that have the same root name as the top level name',
'i_have_a_ref_with_the_same_name',
{
definitions => {'more-bundle2_yaml-definitions_i_have_a_ref_with_the_same_name' => {type => 'string'}},
# begin i_have_a_ref_with_the_same_name definition
type => 'object',
properties => {
name => {type => 'string'},
children => {
type => 'array',
items => {'$ref' => '#/definitions/more-bundle2_yaml-definitions_i_have_a_ref_with_the_same_name'},
default => []
},
},
},
);
bundle_test(
'find and resolve a reference that immediately leaps to another file',
'i_am_a_ref_to_another_file',
{
definitions => {'more-bundle_yaml-definitions_ref3' => {type => 'integer'}},
# begin i_am_a_ref_to_another_file definition - which is actually
# i_have_a_ref_to_the_first_filename
type => 'object',
properties => {gotcha => {'$ref' => '#/definitions/more-bundle_yaml-definitions_ref3'}},
},
);
done_testing;
sub bundle_test {
my ($desc, $schema_name, $expected) = @_;
subtest "$desc - $schema_name" => sub {
my $source = $bundler_validator->schema->data->{definitions}{$schema_name};
my $got = $bundler_validator->bundle({schema => $source});
cmp_deeply($got, $expected, 'extracted schema for ' . $schema_name)
or diag explain({expected => $expected, source => $source, got => $got});
my @errors = $draft7_validator->validate($got);
ok !@errors, 'bundled schema conforms to the draft 7 spec';
my $fresh_draft7_validator = JSON::Validator->new;
$fresh_draft7_validator->load_and_validate_schema($got, {schema => 'http://json-schema.org/draft-07/schema#'});
cmp_deeply(
$fresh_draft7_validator->schema->data,
$expected, 'our generated schema does not lose any data when parsed again by a new validator',
);
};
}
JSON-Validator-5.18/t/stack/ 0000755 0000765 0000024 00000000000 15211411467 015404 5 ustar jhthorsen staff JSON-Validator-5.18/t/stack/Some.pm 0000644 0000765 0000024 00000000632 15210277065 016651 0 ustar jhthorsen staff package Some;
use Mojo::Base -base;
sub j { JSON::Validator->new }
sub validate_age0 { shift->j->schema('data:///age0.json')->validate(shift) }
sub validate_age1 { shift->j->schema('data:///age1.json')->validate(shift) }
1;
__DATA__
@@ age0.json
{
"title": "Some module",
"type": "object",
"properties": {
"age": { "type": "integer", "minimum": 0, "description": "Age in years" }
}
}
JSON-Validator-5.18/t/stack/Some/ 0000755 0000765 0000024 00000000000 15211411467 016307 5 ustar jhthorsen staff JSON-Validator-5.18/t/stack/Some/Module.pm 0000644 0000765 0000024 00000000604 15210277065 020075 0 ustar jhthorsen staff package Some::Module;
use Mojo::Base 'Some';
sub validate_age0 { shift->j->schema('data:///age0.json')->validate(shift) }
sub validate_age1 { shift->j->schema('data://Some::Module/age1.json')->validate(shift) }
1;
__DATA__
@@ age1.json
{
"title": "Some module",
"type": "object",
"properties": {
"age": { "type": "integer", "minimum": 1, "description": "Age in years" }
}
}
JSON-Validator-5.18/t/validate-schema.t 0000644 0000765 0000024 00000002153 15210277065 017517 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $should_fail = JSON::Validator->new->schema('data://main/invalid.json');
my $json_schema = JSON::Validator->new->schema('http://json-schema.org/draft-04/schema#');
my @errors;
# The schema is invalid...
@errors = $json_schema->validate($should_fail->schema->data);
is $errors[0], '/properties/should_fail: Expected object - got array.', 'invalid property element';
# ...but can still be used to validate data.
@errors = $should_fail->validate({foo => 123});
is int(@errors), 0, 'data is valid';
# Can also use load_and_validate_schema() to do the same as above
eval { JSON::Validator->new->load_and_validate_schema('data://main/invalid.json'); };
like $@, qr{Expected object - got array}, 'invalid schema';
done_testing;
__DATA__
@@ invalid.json
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Example Schema That Should Fail To Load",
"description": "There is an array as the value of an object property, which should not be allowed.",
"type": "object",
"properties": {
"foo": { "type": "integer" },
"should_fail": []
}
}
JSON-Validator-5.18/t/openapiv3-default-values.t 0000644 0000765 0000024 00000002352 15210277065 021314 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my @errors;
@errors = $schema->validate_request([get => '/pets/{id}'], {path => {id => 'a'}});
is "@errors", "/id: String is too short: 1/3.", 'invalid id';
@errors = $schema->validate_request([get => '/pets/{id}'], {path => {}});
is "@errors", "", 'default id';
my $id = {};
my %req = (path => sub {$id});
@errors = $schema->validate_request([get => '/pets/{id}'], \%req);
is_deeply $id, {exists => 1, in => 'path', name => 'id', valid => 1, value => 'foo'}, 'input was mutated';
is "@errors", "", 'default id';
is $id->{value}, 'foo', 'coerced default value';
done_testing;
__DATA__
@@ spec.json
{
"openapi": "3.0.0",
"info": { "title": "Style And Explode", "version": "" },
"paths": {
"/pets/{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {"type": "string", "default": "foo", "minLength": 3}
}
],
"responses" : {
"200": {
"description": "pet response",
"schema": {"type": "object"}
}
}
}
}
}
}
JSON-Validator-5.18/t/openapiv3-nullable.t 0000755 0000765 0000024 00000010470 15210277065 020174 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my ($body, @errors);
for my $path (qw(/nullable-data /nullable-ref)) {
$body = {exists => 1, value => {id => 42}};
@errors = $schema->validate_response([get => $path], {body => \&body});
is "@errors", "/body/name: Missing property.", "$path - missing name";
$body = {exists => 1, value => {id => 42, name => undef}};
@errors = $schema->validate_response([get => $path], {body => \&body});
is "@errors", "", "$path - name is undef";
}
for my $extra ({}, undef) {
$body = {exists => 1, value => {extra => $extra, id => 42, name => undef}};
@errors = $schema->validate_response([get => '/nullable-data'], {body => \&body});
is "@errors", "", sprintf 'extra %s', $extra ? 'object' : 'null';
}
for my $stuff ([], undef) {
$body = {exists => 1, value => {stuff => $stuff, id => 42, name => undef}};
@errors = $schema->validate_response([get => '/nullable-data'], {body => \&body});
is "@errors", "", sprintf 'stuff %s', $stuff ? 'array' : 'null';
}
$schema = JSON::Validator->new->schema('data://main/issue-241.json')->schema;
$body = {exists => 1, value => {name => undef}};
@errors = $schema->validate_response([get => '/test'], {body => \&body});
is "@errors", "", "nullable inside oneOf";
$schema = JSON::Validator->new->schema('data://main/nullable-with-enum.json')->schema;
$body = {exists => 1, value => {name => undef}};
@errors = $schema->validate_response([get => '/test'], {body => \&body});
is "@errors", "", "nullable string with enum";
done_testing;
sub body {$body}
__DATA__
@@ spec.json
{
"openapi": "3.0.0",
"info": { "title": "Nullable", "version": "" },
"paths": {
"/nullable-data": {
"get": {
"responses": {
"200": {
"content": { "application/json": { "schema": {"$ref": "#/components/schemas/WithNullable"} } }
}
}
}
},
"/nullable-ref": {
"get": {
"operationId": "withNullableRef",
"responses": {
"200": {
"content": { "application/json": { "schema": {"$ref": "#/components/schemas/WithNullableRef"} } }
}
}
}
}
},
"components": {
"schemas": {
"WithNullable": {
"required": [ "id", "name" ],
"properties": {
"extra": { "type": "object", "nullable": true },
"id": { "type": "integer", "format": "int64" },
"name": { "type": "string", "nullable": true },
"stuff": { "type": "array", "nullable": true }
}
},
"WithNullableRef": {
"required": [ "id", "name" ],
"properties": {
"id": { "type": "integer", "format": "int64" },
"name": { "$ref": "#/components/schemas/WithNullable/properties/name" }
}
}
}
}
}
@@ issue-241.json
{
"openapi": "3.0.0",
"info": { "title": "Nullable", "version": "" },
"paths": {
"/test": {
"get": {
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"oneOf": [
{ "$ref": "#/components/schemas/name1" },
{ "$ref": "#/components/schemas/name2" }
]
}
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"name1": { "type": "string", "nullable": "true" },
"name2": { "type": "integer" } }
}
}
@@ nullable-with-enum.json
{
"openapi": "3.0.0",
"info": { "title": "Nullable", "version": "" },
"paths": {
"/test": {
"get": {
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"$ref": "#/components/schemas/name"
}
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"name": { "type": "string", "nullable": "true", "enum": ["foo", "bar"] }
}
}
}
JSON-Validator-5.18/t/openapiv2-collection-format.t 0000644 0000765 0000024 00000010020 15210277065 022002 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my @errors;
@errors = $schema->validate_request([get => '/pets/{id}'], {path => {id => '42'}});
is "@errors", "", 'collectionFormat csv in path';
for ([csv => ","], [pipes => "|"], [ssv => " "], [tsv => "\t"]) {
my ($name, $sep) = @$_;
my $empty = $name =~ m!^pipes! ? '' : '0';
@errors = $schema->validate_request([get => '/pets'], {query => {"${name}0" => $empty, multir => ''}});
is "@errors", "", "collectionFormat ${name}0 empty string";
@errors = $schema->validate_request([get => '/pets'], {query => {"${name}0" => '42', multir => ''}});
is "@errors", "", "collectionFormat ${name}0 single item";
@errors = $schema->validate_request([get => '/pets'], {query => {"${name}0" => "4${sep}2", multir => ''}});
is "@errors", "", "collectionFormat ${name}0 two item";
@errors = $schema->validate_request([get => '/pets'], {query => {"${name}2" => '42', multir => ''}});
is "@errors", "/${name}2: Not enough items: 1/2.", "collectionFormat ${name}2 single item";
}
done_testing;
__DATA__
@@ spec.json
{
"swagger": "2.0",
"info": {"version": "", "title": "Test collectionFormat"},
"basePath": "/api",
"paths": {
"/pets/{id}": {
"get": {
"parameters": [
{"name": "id", "in": "path", "type": "array", "collectionFormat": "csv", "items": {"type": "string"}, "minItems": 0, "required": true}
],
"responses": {
"200": {
"description": "pet response",
"schema": {"type": "object"}
}
}
}
},
"/pets": {
"get": {
"parameters": [
{"name": "csv", "in": "query", "type": "array", "collectionFormat": "csv", "items": {"type": "number"}, "minItems": 0, "default": []},
{"name": "csv0", "in": "query", "type": "array", "collectionFormat": "csv", "items": {"type": "number"}, "minItems": 0},
{"name": "csv2", "in": "query", "type": "array", "collectionFormat": "csv", "items": {"type": "number"}, "minItems": 2},
{"name": "multi", "in": "query", "type": "array", "collectionFormat": "multi", "items": {"type": "integer"}, "minItems": 0, "default": []},
{"name": "multi0", "in": "query", "type": "array", "collectionFormat": "multi", "items": {"type": "integer"}, "minItems": 0},
{"name": "multi2", "in": "query", "type": "array", "collectionFormat": "multi", "items": {"type": "integer"}, "minItems": 2},
{"name": "multir", "in": "query", "type": "array", "collectionFormat": "multi", "required": true, "items": {"type": "string"}, "minItems":1},
{"name": "pipes", "in": "query", "type": "array", "collectionFormat": "pipes", "items": {"type": "string"}, "minItems": 0, "default": []},
{"name": "pipes0", "in": "query", "type": "array", "collectionFormat": "pipes", "items": {"type": "string"}, "minItems": 0},
{"name": "pipes2", "in": "query", "type": "array", "collectionFormat": "pipes", "items": {"type": "integer"}, "minItems": 2},
{"name": "ssv", "in": "query", "type": "array", "collectionFormat": "ssv", "items": {"type": "number"}, "minItems": 0, "default": []},
{"name": "ssv0", "in": "query", "type": "array", "collectionFormat": "ssv", "items": {"type": "number"}, "minItems": 0},
{"name": "ssv2", "in": "query", "type": "array", "collectionFormat": "ssv", "items": {"type": "number"}, "minItems": 2},
{"name": "tsv", "in": "query", "type": "array", "collectionFormat": "tsv", "items": {"type": "integer"}, "minItems": 0, "default": []},
{"name": "tsv0", "in": "query", "type": "array", "collectionFormat": "tsv", "items": {"type": "integer"}, "minItems": 0},
{"name": "tsv2", "in": "query", "type": "array", "collectionFormat": "tsv", "items": {"type": "integer"}, "minItems": 2}
],
"responses": {
"200": {
"description": "pet response",
"schema": {"type": "object"}
}
}
}
}
}
}
JSON-Validator-5.18/t/jv-string.t 0000644 0000765 0000024 00000002626 15210277065 016420 0 ustar jhthorsen staff use lib '.';
use utf8;
use t::Helper;
my $schema = {type => 'object',
properties => {nick => {type => 'string', minLength => 3, maxLength => 10, pattern => qr{^\w+$}}}};
validate_ok {nick => 'batman'}, $schema;
validate_ok {nick => 1000}, $schema, E('/nick', 'Expected string - got number.');
validate_ok {nick => '1000'}, $schema;
validate_ok {nick => 'aa'}, $schema, E('/nick', 'String is too short: 2/3.');
validate_ok {nick => 'a' x 11}, $schema, E('/nick', 'String is too long: 11/10.');
like +join('', jv->validate({nick => '[nick]'})), qr{/nick: String does not match}, 'String does not match';
delete $schema->{properties}{nick}{pattern};
validate_ok {nick => 'Déjà vu'}, $schema;
jv->coerce('str');
validate_ok {nick => 1000}, $schema;
# https://github.com/mojolicious/json-validator/issues/134
validate_ok(
{credit_card_number => '5252525252525252'},
{
type => "object",
required => ["credit_card_number"],
properties => {credit_card_number => {type => "string", minLength => 15, maxLength => 16}}
}
);
my $string_constant = {type => 'string', const => 'foo'};
validate_ok 'foo', $string_constant;
validate_ok 'bar', $string_constant, E('/', q{Does not match const: "foo".});
my $empty_string_constant = {type => 'string', const => ''};
validate_ok '', $empty_string_constant;
validate_ok 'bar', $empty_string_constant, E('/', q{Does not match const: "".});
done_testing;
JSON-Validator-5.18/t/benchmark.t 0000644 0000765 0000024 00000003755 15210277065 016433 0 ustar jhthorsen staff BEGIN { $ENV{JSON_VALIDATOR_WARN_MISSING_FORMAT} = 0 }
use Mojo::Base -strict;
use Benchmark qw(cmpthese timeit :hireswallclock);
use JSON::Validator::Schema::Draft7;
use List::Util qw(sum);
use Test::More;
use Time::HiRes qw(time);
plan skip_all => 'TEST_BENCHMARK=500' unless my $n = $ENV{TEST_BENCHMARK};
diag sprintf "\n%s", scalar localtime;
diag "n_times=$n";
my %bm;
time_schema('defaults' => {});
time_schema('resolve_before' => {resolve_before => 1});
cmpthese \%bm if $ENV{HARNESS_IS_VERBOSE};
done_testing;
sub time_schema {
my ($desc, $attrs) = @_;
my (@errors, @resolve_t, @validate_t, @total_t);
my $resolve_before = delete $attrs->{resolve_before};
my $resolved_schema
= $resolve_before && JSON::Validator::Schema::Draft7->new('http://json-schema.org/draft-07/schema#', %$attrs);
$bm{$desc} = timeit 1 => sub {
for (1 ... $n) {
my $schema = $resolved_schema || JSON::Validator::Schema::Draft7->new(%$attrs);
my $t0 = time;
delete $schema->{errors};
$schema->resolve('http://json-schema.org/draft-07/schema#') unless $resolve_before;
push @resolve_t, (my $t1 = time) - $t0;
push @errors, @{$schema->errors};
push @validate_t, (my $t2 = time) - $t1;
push @total_t, $t2 - $t0;
}
};
ok !@errors, 'valid schema' or diag "@errors";
my $rt = sprintf '%.3f', sum @resolve_t;
ok $rt < 2, "$desc - resolve ${rt}s" unless $resolve_before;
my $vt = sprintf '%.3f', sum @validate_t;
ok $vt < 2, "$desc - validate ${vt}s";
my $tt = sprintf '%.3f', sum @total_t;
ok $tt < 2, "$desc - total ${tt}s";
}
__DATA__
# Tue Jul 20 07:25:46 2021
# n_times=200
ok 1 - valid schema
ok 2 - defaults - resolve 0.560s
ok 3 - defaults - validate 1.053s
ok 4 - defaults - total 1.613s
ok 5 - valid schema
ok 6 - resolve_before - validate 1.030s
ok 7 - resolve_before - total 1.030s
s/iter defaults resolve_before
defaults 1.61 -- -37%
resolve_before 1.02 58% --
JSON-Validator-5.18/t/openapiv3-style-explode.t 0000644 0000765 0000024 00000024673 15210277065 021203 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv3;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/schema.json')->schema;
test('get /array/label{id}' => [{path => {id => '.3,4,5'}}, {id => [3, 4, 5]}], [{path => {id => '5'}}, {id => [5]}]);
test(
'get /array/label/explode{id}',
[{path => {id => '.3.4.5'}}, {id => [3, 4, 5]}],
[{path => {id => '.5'}}, {id => [5]}],
);
test(
'get /array/matrix{id}',
[{path => {id => ';id=3,4,5'}}, {id => [3, 4, 5]}],
[{path => {id => ';id=5'}}, {id => [5]}],
);
test(
'get /array/matrix/explode{id}',
[{path => {id => ';id=3;id=4;id=5'}}, {id => [3, 4, 5]}],
[{path => {id => ';id=5'}}, {id => [5]}],
);
test(
'get /array/query',
[{}, {}, '/ri: Missing property.'],
[{query => {ri => '1.3'}}, {ri => 'ri'}, '/ri/0: Expected integer - got string.'],
[{query => {ml => 5, ri => '0'}}, {ml => 'ml', ri => [0]}, '/ml: Not enough items: 1/2.'],
[{query => {ml => ['3', 5], ri => '0'}}, {ml => [3, 5], ri => [0]}, ''],
[{query => {pi => '1|2|3', ri => '0'}}, {pi => [1, 2, 3], ri => [0]}, ''],
[{query => {ri => '0', sp => '2 3 4'}}, {ri => [0], sp => [2, 3, 4]}, '']
);
test(
'get /array/simple/{id}',
[{}, {}, '/id: Missing property.'],
[{path => {id => '10'}}, {id => [10]}],
[{path => {id => '10,20'}}, {id => [10, 20]}]
);
test('get /object/label{id}',
[{path => {id => '.category.bird.name.birdy'}}, {id => {category => 'bird', name => 'birdy'}}]);
test('get /object/label/explode{id}',
[{path => {id => '.category=bird.name=birdy'}}, {id => {category => 'bird', name => 'birdy'}}]);
test('get /object/matrix{id}',
[{path => {id => ';id=category,bird,name,birdy'}}, {id => {category => 'bird', name => 'birdy'}}]);
test('get /object/matrix/explode{id}',
[{path => {id => ';category=bird;name=birdy'}}, {id => {category => 'bird', name => 'birdy'}}]);
test(
'get /object/query',
[{}, {}, ''],
[{query => {ff => ''}}, {all => {ff => ['']}, ff => {}}],
[{query => {pf => ''}}, {all => {pf => ''}, pf => {}}],
[{query => {sf => ''}}, {all => {sf => ''}, sf => {}}],
[{query => {ff => 'name,birdy,age,1'}}, {all => {ff => ['name,birdy,age,1']}, ff => {age => 1, name => 'birdy'}}],
[{query => {pf => 'name|birdy|age|2'}}, {all => {pf => 'name|birdy|age|2'}, pf => {age => 2, name => 'birdy'}}],
[{query => {sf => 'name birdy age 3'}}, {all => {sf => 'name birdy age 3'}, sf => {age => 3, name => 'birdy'}}],
);
test(
'get /object/query',
[
{query => {'do[name]' => 'birdy', 'do[birth-date][gte]' => '1970-01-01', 'do[numbers][0]' => '5'}},
{
all => {'do[name]' => 'birdy', 'do[birth-date][gte]' => '1970-01-01', 'do[numbers][0]' => 5},
do => {name => 'birdy', 'birth-date' => {gte => '1970-01-01'}, numbers => [5]},
},
],
[
{query => {'do[0][1][0]' => 2, 'do[2][0]' => 4}},
{all => {'do[0][1][0]' => 2, 'do[2][0]' => 4}, do => {0 => [undef, [2]], 2 => [4]}},
],
[
{query => {'do[numbers][1]' => 2, 'do[numbers][0]' => '4'}},
{all => {'do[numbers][0]' => 4, 'do[numbers][1]' => 2}, do => {numbers => [4, 2]}},
],
[{query => {'do[numbers][]' => [3, '5']}}, {all => {'do[numbers][]' => [3, 5]}, do => {numbers => [3, 5]}}],
[{query => {'do[numbers]' => [4, 6]}}, {all => {'do[numbers]' => [4, 6]}, do => {numbers => [4, 6]}}],
);
test('get /object/simple/{id}',
[{path => {id => 'category,bird,name,birdy'}}, {id => {category => 'bird', name => 'birdy'}}]);
test('get /object/simple/explode/{id}',
[{path => {id => 'category=bird,name=birdy'}}, {id => {category => 'bird', name => 'birdy'}}]);
done_testing;
sub test {
my ($path, @tests) = @_;
subtest "path $path" => sub {
for (@tests) {
my ($input, $exp, $err) = @$_;
my (%mutated, %req);
for my $in (keys %$input) {
$req{$in} = sub {
my ($name, $param) = @_;
return $mutated{$param->{name}}
= defined $name
? {exists => exists $input->{$in}{$name}, value => $input->{$in}{$name}}
: {exists => 1, value => {map { ($_ => $input->{$in}{$_}) } keys %{$input->{$in}}}};
};
}
my @errors = $schema->validate_request([split ' ', $path], \%req);
is "@errors", $err || '', sprintf 'validate %s', Mojo::JSON::to_json($exp);
delete $mutated{$_} for grep { !defined $mutated{$_}{valid} } keys %mutated;
$mutated{$_}{value} = $mutated{$_}{name} for grep { !$mutated{$_}{valid} } keys %mutated;
$mutated{$_} = $mutated{$_}{value} for keys %mutated;
is_deeply \%mutated, $exp, sprintf 'mutated %s', Mojo::JSON::to_json($exp);
}
};
}
__DATA__
@@ schema.json
{
"openapi": "3.0.0",
"info": { "title": "Style And Explode", "version": "" },
"paths": {
"/array/label{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "label",
"explode": false,
"schema": { "type": "array", "items": { "type": "integer" }, "minItems": 1 }
}
]
}
},
"/array/label/explode{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "label",
"explode": true,
"schema": { "type": "array", "items": { "type": "integer" }, "minItems": 1 }
}
]
}
},
"/array/matrix{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "matrix",
"explode": false,
"schema": { "type": "array", "items": { "type": "integer" }, "minItems": 1 }
}
]
}
},
"/array/matrix/explode{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "matrix",
"explode": true,
"schema": { "type": "array", "items": { "type": "integer" }, "minItems": 1 }
}
]
}
},
"/array/simple/{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "simple",
"schema": { "type": "array", "items": { "type": "integer" }, "minItems": 1 }
}
]
}
},
"/array/query": {
"get": {
"parameters": [
{
"name": "ml",
"in": "query",
"style": "form",
"explode": true,
"schema": { "type": "array", "items": { "type": "string" }, "minItems": 2 }
},
{
"name": "ri",
"in": "query",
"required": true,
"style": "form",
"explode": true,
"schema": { "type": "array", "items": { "type": "integer" }, "minItems": 1 }
},
{
"name": "sp",
"in": "query",
"style": "spaceDelimited",
"schema": { "type": "array", "items": { "type": "integer" } }
},
{
"name": "pi",
"in": "query",
"style": "pipeDelimited",
"schema": { "type": "array", "items": { "type": "integer" } }
}
]
}
},
"/object/label{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "label",
"explode": false,
"schema": { "type": "object" }
}
]
}
},
"/object/label/explode{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "label",
"explode": true,
"schema": { "type": "object" }
}
]
}
},
"/object/matrix{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "matrix",
"explode": false,
"schema": { "type": "object" }
}
]
}
},
"/object/matrix/explode{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "matrix",
"explode": true,
"schema": { "type": "object" }
}
]
}
},
"/object/query": {
"get": {
"parameters": [
{
"name": "do",
"in": "query",
"style": "deepObject",
"explode": true,
"schema": { "type": "object" }
},
{
"name": "ff",
"in": "query",
"style": "form",
"explode": false,
"schema": { "type": "object" }
},
{
"name": "all",
"in": "query",
"style": "form",
"explode": true,
"schema": {
"type": "object",
"properties": {
"ff": {"type": "array", "items": {"type": "string"}}
}
}
},
{
"name": "sf",
"in": "query",
"style": "spaceDelimited",
"explode": false,
"schema": { "type": "object" }
},
{
"name": "pf",
"in": "query",
"style": "pipeDelimited",
"explode": false,
"schema": { "type": "object" }
}
]
}
},
"/object/simple/{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "simple",
"explode": false,
"schema": { "type": "object" }
}
]
}
},
"/object/simple/explode/{id}": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"style": "simple",
"explode": true,
"schema": { "type": "object" }
}
]
}
}
}
}
JSON-Validator-5.18/t/definitions/ 0000755 0000765 0000024 00000000000 15211411467 016612 5 ustar jhthorsen staff JSON-Validator-5.18/t/definitions/weight.json 0000644 0000765 0000024 00000000235 15210277065 020777 0 ustar jhthorsen staff {
"type": "object",
"description": "Weight with Units",
"properties": {
"mass": { "type": "integer" },
"unit": { "$ref": "./unit.json" }
}
}
JSON-Validator-5.18/t/definitions/space age.json 0000644 0000765 0000024 00000000111 15210277065 021311 0 ustar jhthorsen staff {
"type": "integer",
"minimum": 0,
"description": "Age in years"
}
JSON-Validator-5.18/t/definitions/age.json 0000644 0000765 0000024 00000000111 15210277065 020235 0 ustar jhthorsen staff {
"type": "integer",
"minimum": 0,
"description": "Age in years"
}
JSON-Validator-5.18/t/definitions/unit.json 0000644 0000765 0000024 00000000123 15210277065 020463 0 ustar jhthorsen staff {
"type": "string",
"description": "Unit of Mass",
"pattern": "^kg|st|lb$"
}
JSON-Validator-5.18/t/get.t 0000644 0000765 0000024 00000005463 15210277065 015256 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::Draft201909;
use Test::More;
my $jv;
subtest 'setup' => sub {
$jv = JSON::Validator::Schema::Draft201909->new({
'$defs' => {z1 => {'$ref' => '#/$defs/z2', minLength => 1}, z2 => {type => 'string'}},
properties => {
bar => {items => [{properties => {y => {'$ref' => '#/$defs/z1'}, x => {type => 'integer'}}}]},
foo => {items => [{properties => {y => {type => 'string'}}}]},
'x/~y' => {type => 'boolean'},
},
});
ok !$jv->is_invalid, 'schema is valid' or diag explain $jv->errors;
};
subtest 'get($string)' => sub {
is $jv->get('/properties/foo/items/0/properties/y/type'), 'string', 'get /properties/foo/items/0/properties/y/type';
is $jv->get('/$defs/baz'), undef, 'get /$defs/baz';
is $jv->get('/properties/baz'), undef, 'get /properties/baz';
is $jv->get('/properties/baz'), undef, 'get /properties/baz';
is $jv->get('/properties/x~1~0y/type'), 'boolean', 'get /x~1y';
};
subtest 'get(\@array)' => sub {
is $jv->get([qw(properties foo items 0 properties y type)]), 'string',
'get /properties/foo/items/0/properties/y/type';
is $jv->get([qw($defs baz)]), undef, 'get /$defs/baz';
is $jv->get([qw(properties baz)]), undef, 'get /properties/baz';
is $jv->get([qw(properties x/~y type)]), 'boolean', 'get /properties/x/~y type';
};
subtest '$ref' => sub {
is_deeply $jv->get('/properties/bar/items/0/properties/y'), {minLength => 1, type => 'string'},
'get /bar/items/0/properties/y';
is $jv->get('/properties/bar/items/0/properties/y/$ref'), '#/$defs/z1', 'get /bar/items/0/properties/y/$ref';
is_deeply $jv->get('/properties/bar/items/0/properties'), {y => {'$ref' => '#/$defs/z1'}, x => {type => 'integer'}},
'get /bar/items/0/properties';
};
subtest 'callback' => sub {
my @res;
$jv->get(['properties', undef, 'items', '0', 'properties', undef, 'type'], sub { push @res, [@_] });
is @res, 3, 'callback called';
is_deeply \@res,
[
['integer', '/properties/bar/items/0/properties/x/type'],
['string', '/properties/bar/items/0/properties/y/type'],
['string', '/properties/foo/items/0/properties/y/type'],
],
'callback data';
};
subtest 'collection' => sub {
note 'This is not officially supported. I think the callback version is the way to go.';
is_deeply $jv->get(['properties', 'bar', 'items', '0', 'properties', undef, 'type']), ['integer', 'string'],
'one level';
my $c = $jv->get(['properties', undef, 'items', '0', 'properties', undef, 'type']);
is $c->first->first, 'integer', 'collections of collections';
is_deeply $c->flatten->to_array, ['integer', 'string', 'string', undef], 'flatten' or diag explain $c;
};
done_testing;
JSON-Validator-5.18/t/openapiv2-file.t 0000644 0000765 0000024 00000001744 15210277065 017315 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.json')->schema;
my ($form, @errors);
for my $image (undef, '') {
$form = {image => '', id => 'i1'};
@errors = $schema->validate_request([post => '/pets'], {formData => $form});
is "@errors", '/image: Missing property.', 'missing image';
}
$form = {image => '0', id => 'i1'};
@errors = $schema->validate_request([post => '/pets'], {formData => $form});
is "@errors", '', 'valid input';
done_testing;
__DATA__
@@ spec.json
{
"swagger": "2.0",
"info": {"version": "0.8", "title": "Test body"},
"basePath": "/api",
"paths": {
"/pets": {
"post": {
"parameters": [
{"name": "image", "in": "formData", "type": "file", "required": true},
{"name": "id", "in": "formData", "type": "string"}
],
"responses": {
"200": {"description": "ok", "schema": {"type": "object"}}
}
}
}
}
}
JSON-Validator-5.18/t/jv-integer.t 0000644 0000765 0000024 00000002270 15210277065 016542 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema = {type => 'object', properties => {mynumber => {type => 'integer', minimum => 1, maximum => 4}}};
validate_ok {mynumber => 1}, $schema;
validate_ok {mynumber => 4}, $schema;
validate_ok {mynumber => 2}, $schema;
validate_ok {mynumber => 0}, $schema, E('/mynumber', '0 < minimum(1)');
validate_ok {mynumber => -1}, $schema, E('/mynumber', '-1 < minimum(1)');
validate_ok {mynumber => 5}, $schema, E('/mynumber', '5 > maximum(4)');
validate_ok {mynumber => '2'}, $schema, E('/mynumber', 'Expected integer - got string.');
$schema->{properties}{mynumber}{multipleOf} = 2;
validate_ok {mynumber => 3}, $schema, E('/mynumber', 'Not multiple of 2.');
my $int_constant = {type => 'integer', const => 2};
validate_ok 2, $int_constant;
validate_ok 1, $int_constant, E('/', q{Does not match const: 2.});
jv->coerce('num');
validate_ok {mynumber => '2'}, $schema;
validate_ok {mynumber => '2xyz'}, $schema, E('/mynumber', 'Expected integer - got string.');
$schema->{properties}{mynumber}{minimum} = -3;
validate_ok {mynumber => '-2'}, $schema;
validate_ok '2', $int_constant;
validate_ok '1', $int_constant, E('/', q{Does not match const: 2.});
done_testing;
JSON-Validator-5.18/t/draft6.t 0000644 0000765 0000024 00000001466 15210277065 015664 0 ustar jhthorsen staff use lib '.';
use t::Helper;
use JSON::Validator::Schema::Draft6;
t::Helper->schema(JSON::Validator::Schema::Draft6->new);
t::Helper->test(number => qw(basic maximum minimum));
t::Helper->test(array => qw(basic items additional_items contains min_max unique));
t::Helper->test(object => qw(basic properties));
t::Helper->test(object => qw(additional_properties pattern_properties min_max names));
subtest 'exclusiveMaximum' => sub {
schema_validate_ok 2.4, {exclusiveMaximum => 2.4}, E('/', '2.4 >= maximum(2.4)');
schema_validate_ok 0, {exclusiveMaximum => 0}, E('/', '0 >= maximum(0)');
};
subtest 'exclusiveMinimum' => sub {
schema_validate_ok 4.2, {exclusiveMinimum => 4.2}, E('/', '4.2 <= minimum(4.2)');
schema_validate_ok 0, {exclusiveMinimum => 0}, E('/', '0 <= minimum(0)');
};
done_testing;
JSON-Validator-5.18/t/jv-number.t 0000644 0000765 0000024 00000003011 15210277065 016367 0 ustar jhthorsen staff use lib '.';
use t::Helper;
my $schema = {type => 'object', properties => {mynumber => {type => 'number', minimum => -0.5, maximum => 2.7}}};
validate_ok {mynumber => 1}, $schema;
validate_ok {mynumber => '2'}, $schema, E('/mynumber', 'Expected number - got string.');
my $numeric_constant = {type => 'number', const => 2.1};
validate_ok 2.1, $numeric_constant;
validate_ok 1, $numeric_constant, E('/', q{Does not match const: 2.1.});
jv->coerce('numbers');
validate_ok {mynumber => '-0.5'}, $schema;
validate_ok {mynumber => -0.6}, $schema, E('/mynumber', '-0.6 < minimum(-0.5)');
validate_ok {mynumber => '2.7'}, $schema;
validate_ok {mynumber => '2.8'}, $schema, E('/mynumber', '2.8 > maximum(2.7)');
validate_ok {mynumber => '0.1e+1'}, $schema;
validate_ok {mynumber => '2xyz'}, $schema, E('/mynumber', 'Expected number - got string.');
validate_ok {mynumber => '.1'}, $schema, E('/mynumber', 'Expected number - got string.');
validate_ok {validNumber => 2.01},
{type => 'object', properties => {validNumber => {type => 'number', multipleOf => 0.01}}};
validate_ok '2.1', $numeric_constant;
validate_ok '1', $numeric_constant, E('/', q{Does not match const: 2.1.});
for my $x ([-0.5, 2.7], [true, true]) {
$schema->{properties}{mynumber}{exclusiveMaximum} = $x->[1];
$schema->{properties}{mynumber}{exclusiveMinimum} = $x->[0];
validate_ok {mynumber => 2.7}, $schema, E('/mynumber', '2.7 >= maximum(2.7)');
validate_ok {mynumber => -0.5}, $schema, E('/mynumber', '-0.5 <= minimum(-0.5)');
}
done_testing;
JSON-Validator-5.18/t/predictable-errors.t 0000644 0000765 0000024 00000001500 15210277065 020253 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $jv = JSON::Validator->new;
my $broken_data = {ant => [qw(fire soldier termite)], bat => 'cricket', cat => 'lion', dog => 'good boy'};
my $num_errors;
# The schema below gets turned into a perl hash inside JSON::Validator,
# so looping around like this will execute the test with all kinds of
# different internal ordering
for (1 .. 20) {
my $schema_text
= '{"type":"object","properties":{"ant":{"type":"string"},"bat":{"type":"array"},"cat":{"type":"object"},"dog":{"type":"integer"}}}';
$jv->schema($schema_text);
is_deeply [map { $_->path } $jv->validate($broken_data)], [qw(/ant /bat /cat /dog)], 'got errors in expected order';
is scalar $jv->validate($broken_data), 4, 'in scalar context got the right number of errors';
}
done_testing;
JSON-Validator-5.18/t/openapiv3-content-types.t 0000644 0000765 0000024 00000020100 15210277065 021176 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my $schema = JSON::Validator->new->schema('data://main/spec.yaml')->schema;
my ($body, @errors);
sub body {$body}
subtest '*/* w/ required body' => sub {
subtest 'content-type is missing' => sub {
$body = {exists => 0};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is empty string' => sub {
$body = {exists => 0, content_type => ''};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}, content_type => ''};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}, content_type => ''};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is application/json' => sub {
$body = {exists => 0};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}, content_type => 'application/json'};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}, content_type => 'application/json'};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is application/json; charset=utf-8' => sub {
$body = {exists => 0};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}, content_type => 'application/json; charset=utf-8'};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}, content_type => 'application/json; charset=utf-8'};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
};
subtest 'application/json w/ body' => sub {
subtest 'content-type is missing' => sub {
$body = {exists => 0};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is empty string' => sub {
$body = {exists => 0, content_type => ''};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}, content_type => ''};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}, content_type => ''};
@errors = $schema->validate_request([post => '/pets_any'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is application/json' => sub {
$body = {exists => 0};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body: Missing property.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}, content_type => 'application/json'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}, content_type => 'application/json'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is application/json; charset=utf-8' => sub {
$body = {exists => 1, value => {name => 'kitty'}, content_type => 'application/json; charset=utf-8'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 1, value => {age => 42}, content_type => 'application/json; charset=utf-8'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body/name: Missing property.", 'invalid request body';
};
subtest 'content-type is application/xml' => sub {
$body = {exists => 0, content_type => 'application/xml'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body: Expected application/json - got application/xml.", 'invalid request body';
$body = {exists => 1, value => {name => 'kitty'}, content_type => 'application/xml'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body: Expected application/json - got application/xml.", 'invalid request body';
$body = {exists => 1, value => {age => 42}, content_type => 'application/xml'};
@errors = $schema->validate_request([post => '/pets_json'], {body => \&body});
is "@errors", "/body: Expected application/json - got application/xml.", 'invalid request body';
};
};
subtest 'w/o body' => sub {
$body = {exists => 0};
@errors = $schema->validate_request([post => '/pets/publish'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 0, content_type => ''};
@errors = $schema->validate_request([post => '/pets/publish'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 0, content_type => 'application/xml'};
@errors = $schema->validate_request([post => '/pets/publish'], {body => \&body});
is "@errors", "", 'valid request body';
$body = {exists => 0, content_type => 'application/json; charset=utf-8'};
@errors = $schema->validate_request([post => '/pets/publish'], {body => \&body});
is "@errors", "", 'valid request body';
};
done_testing;
__DATA__
@@ spec.yaml
openapi: 3.0.0
info:
title: Style And Explode
version: ""
paths:
/pets_any:
post:
requestBody:
required: true
content:
"*/*":
schema:
type: object
required:
- name
properties:
name:
type: string
responses:
"200":
description: success
schema:
type: object
/pets_json:
post:
requestBody:
required: true
content:
"application/json":
schema:
type: object
required:
- name
properties:
name:
type: string
responses:
"200":
description: success
schema:
type: object
/pets/publish:
post:
description: example of endpoint with no request body
responses:
"200":
description: success
schema:
type: object
JSON-Validator-5.18/t/to-json.t 0000644 0000765 0000024 00000002433 15210277065 016062 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use Test::More;
my @errors = JSON::Validator->new->schema('data://main/error_object.json')
->validate(bless({path => '', message => 'yikes'}, 'JSON::Validator::Error'));
ok !@errors, 'TO_JSON on objects' or diag join ', ', @errors;
my $input = {
errors => [JSON::Validator::Error->new('/', 'foo'), JSON::Validator::Error->new('/', 'bar')],
valid => Mojo::JSON->false,
};
@errors = JSON::Validator->new->schema('data://main/error_array.json')->validate($input);
ok !@errors, 'TO_JSON on objects inside arrays' or diag join ', ', @errors;
is_deeply $input,
{
errors => [JSON::Validator::Error->new('/', 'foo'), JSON::Validator::Error->new('/', 'bar')],
valid => Mojo::JSON->false,
},
'input objects are not changed';
done_testing;
__DATA__
@@ error_object.json
{
"type": "object",
"properties": { "message": { "type": "string" } },
"required": ["message"]
}
@@ error_array.json
{
"type": "object",
"required": [ "errors" ],
"properties": {
"valid": { "type": "boolean" },
"errors": {
"type": "array",
"items": {
"type": "object",
"required": [ "message" ],
"properaties": {
"message": { "type": "string" },
"path": { "type": "string" }
}
}
}
}
}
JSON-Validator-5.18/t/openapiv3-bundled-spec.t 0000644 0000765 0000024 00000003051 15211411261 020722 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator;
use JSON::Validator::Schema::OpenAPIv3;
use Test::More;
# Issue #286: the default OpenAPI v3.0 specification must be bundled with the
# distribution and resolvable offline. Any attempt to fetch it over the network
# means the bundled spec is missing.
subtest 'default specification is the bundled 2021-09-28 spec' => sub {
my $schema = JSON::Validator::Schema::OpenAPIv3->new;
is $schema->specification, 'https://spec.openapis.org/oas/3.0/schema/2021-09-28', 'specification';
};
subtest 'default specification loads from the bundle without network' => sub {
my $jv = JSON::Validator->new;
$jv->store->ua(FakeUA->new); # blow up if a network request is attempted
my $url = 'https://spec.openapis.org/oas/3.0/schema/2021-09-28';
my $id = $jv->store->load($url);
is $id, $url, 'loaded from bundle';
is $jv->store->get($id)->{id}, $url, 'bundled schema id matches url';
};
subtest 'openapi 3.0.x documents are validated against the bundled default' => sub {
my $jv = JSON::Validator->new;
$jv->store->ua(FakeUA->new); # blow up if a network request is attempted
my $schema = $jv->schema({openapi => '3.0.3', info => {title => 't', version => '1'}, paths => {}})->schema;
is $schema->specification, 'https://spec.openapis.org/oas/3.0/schema/2021-09-28', 'detected default';
is_deeply $schema->errors, [], 'valid minimal 3.0 document';
};
done_testing;
package FakeUA;
sub new { bless {}, shift }
sub get { Test::More::BAIL_OUT('network access attempted - bundled OpenAPI v3.0 spec is missing') }
JSON-Validator-5.18/t/openapiv2-routes.t 0000644 0000765 0000024 00000004402 15210277065 017711 0 ustar jhthorsen staff use Mojo::Base -strict;
use JSON::Validator::Schema::OpenAPIv2;
use Test::Deep;
use Test::More;
my $schema = JSON::Validator::Schema::OpenAPIv2->new;
$schema->data({
paths => {
'/a1' => {get => {}},
'/a1/bbbbbbb2/{c3}' => {post => {}},
'/a1/bbbbbbbbbbbbbbbbbbbb2/{ccc3}' => {put => {}},
'/a1/xxxxxxxxx/{ccc3}' => {get => {}},
'/a1/{b2}/{ccc3}/{d4}' => {post => {}},
'/a1/{bb2}/{c3}/d' => {get => {}},
'/a1/{bb2}/{ccc3}/{dddd4}/{e5}' => {put => {}},
'/a1/{bbbb2}/{cc3}' => {get => {}},
'/aa1/bbb2/{c3}' => {post => {}},
'/aaa1/bb2' => {get => {}},
'/aaa2' => {put => {}},
'/{aaa1}/{bb2}/{ccc3}' => {get => {}},
'/{x}' => {delete => {}},
},
});
is_deeply(
$schema->routes->to_array,
[
{path => '/a1/{bb2}/{ccc3}/{dddd4}/{e5}', method => 'put', operation_id => undef},
{path => '/a1/{bb2}/{c3}/d', method => 'get', operation_id => undef},
{path => '/a1/{b2}/{ccc3}/{d4}', method => 'post', operation_id => undef},
{path => '/a1/bbbbbbb2/{c3}', method => 'post', operation_id => undef},
{path => '/a1/bbbbbbbbbbbbbbbbbbbb2/{ccc3}', method => 'put', operation_id => undef},
{path => '/a1/xxxxxxxxx/{ccc3}', method => 'get', operation_id => undef},
{path => '/aa1/bbb2/{c3}', method => 'post', operation_id => undef},
{path => '/a1/{bbbb2}/{cc3}', method => 'get', operation_id => undef},
{path => '/{aaa1}/{bb2}/{ccc3}', method => 'get', operation_id => undef},
{path => '/aaa1/bb2', method => 'get', operation_id => undef},
{path => '/a1', method => 'get', operation_id => undef},
{path => '/aaa2', method => 'put', operation_id => undef},
{path => '/{x}', method => 'delete', operation_id => undef},
],
'sorted routes'
) or diag explain $schema->routes->map(sub { $_->{path} })->to_array;
done_testing;
JSON-Validator-5.18/CONTRIBUTING.md 0000644 0000765 0000024 00000006124 15210277065 016273 0 ustar jhthorsen staff # HOW TO CONTRIBUTE
Thank you for considering contributing to this distribution. This file
contains instructions that will help you work with the source code.
## Getting dependencies
If you have App::cpanminus installed, you can use
[cpanm](https://metacpan.org/pod/cpanm) to satisfy dependencies like this:
cpanm --installdeps --with-develop .
You can also run this command (or any other cpanm command) without installing
App::cpanminus first, using the fatpacked `cpanm` script via curl or wget:
curl -L https://cpanmin.us | perl - --installdeps --with-develop .
wget -qO - https://cpanmin.us | perl - --installdeps --with-develop .
Otherwise, look for either a `cpanfile` or `META.json` file for a list of
dependencies to satisfy.
There are also some optional modules which should be installe if you
are contributing a code change:
cpanm boolean
cpanm Sereal::Encoder 4.00
cpanm Test::JSON::Schema::Acceptance 1.000
cpanm YAML::XS 0.67
cpanm Test::Pod
cpanm Test::Pod::Coverage
## Running tests
You can run tests directly using the `prove` tool:
prove -l
prove -lv t/some_test_file.t
For most of my distributions, `prove` is entirely sufficient for you to test
any patches you have. I use `prove` for 99% of my testing during development.
## Reporting bugs
First of all, make sure you are using the latest version of JSON::Validator and
its dependencies, it is quite likely that your bug has already been fixed. If
that doesn't help, take a look at the list of currently open issues, perhaps it
has already been reported by someone else and you can just add a comment
confirming it.
If it hasn't been reported yet, try to prepare a test case demonstrating the
bug, you are not expected to fix it yourself, but you'll have to make sure the
developers can replicate your problem. Sending in your whole application
generally does more harm than good, the t directory of this distribution has
many good examples for how to do it right. Writing a test is usually the
hardest part of fixing a bug, so the better your test case the faster it can be
fixed.
And don't forget to add a descriptive title and text, when you create a new
issue. If your issue does not contain enough information or is unintelligible,
it might get closed pretty quickly. But don't be disheartened, if there's new
activity it will get reopened just as quickly.
## Code style
The code style is enforced with a `.perltidyrc` in the project root. Any pull
request or patch should be run through
[Perl::Tidy](https://metacpan.org/pod/distribution/Perl-Tidy/bin/perltidy)
before submitted. This can easily be enforced using a tool such as
[githook-perltidy](https://metacpan.org/pod/githook-perltidy).
## Changes file
Do not change the `Changes` file when working on a patch or "pull request".
This file will be updated appropriately when a new release is made.
# CREDITS
This file was adapted from an initial `CONTRIBUTING.md` file from
[Mojo::SQLite](https://github.com/Grinnz/Mojo-SQLite/blob/master/CONTRIBUTING.md),
and paragraphs as heavily influenced by https://docs.mojolicious.org/Mojolicious/Guides/Contributing.
JSON-Validator-5.18/META.yml 0000644 0000765 0000024 00000005216 15211411470 015303 0 ustar jhthorsen staff ---
abstract: 'Validate data against a JSON schema'
author:
- 'Jan Henning Thorsen '
build_requires:
ExtUtils::MakeMaker: '0'
Test::Deep: '0'
Test::More: '1.30'
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 0
generated_by: 'ExtUtils::MakeMaker version 7.76, CPAN::Meta::Converter version 2.150010'
license: artistic_2
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: JSON-Validator
no_index:
directory:
- t
- inc
- examples
- t
requires:
Data::Validate::Domain: '0.11'
Data::Validate::IP: '0.27'
List::Util: '1.45'
Mojolicious: '9.34'
Net::IDN::Encode: '2.500'
YAML::XS: '0.67'
perl: '5.016'
resources:
IRC:
url: irc://irc.libera.chat/#perl-openapi
web: https://web.libera.chat/#perl-openapi
bugtracker: https://github.com/jhthorsen/json-validator/issues
homepage: https://github.com/jhthorsen/json-validator
license: http://www.opensource.org/licenses/artistic-license-2.0
repository: https://github.com/jhthorsen/json-validator.git
version: '5.18'
x_contributors:
- 'Aleksandr Orlenko '
- 'Alexander Hartmaier '
- 'Alexander Karelas '
- 'Bernhard Graf '
- 'Brad Barden '
- 'Dagfinn Ilmari Mannsåker '
- 'Daniel Böhmer '
- 'David Cantrell '
- 'Ed J '
- 'Ere Maijala '
- 'Fabrizio Gennari '
- 'Ilya Rassadin '
- 'Jan Henning Thorsen '
- 'Jason Cooper '
- 'Karen Etheridge '
- 'Kenichi Ishigaki '
- 'Kevin M. Goess '
- 'Kirill Matusov '
- 'Krasimir Berov '
- 'Lari Taskula '
- 'Lee Johnson '
- 'Martin Renvoize '
- 'Mattias Päivärinta '
- 'Michael Jemmeson '
- 'Michael Schout '
- 'Mohammad S Anwar '
- 'Nick Morrott '
- 'Pierre-Aymeric Masse '
- 'Roy Storey '
- 'Russell Jenkins '
- 'Sebastian Riedel '
- 'Stephan Hradek '
- 'Tim Stallard '
- 'Zoffix Znet '
x_serialization_backend: 'CPAN::Meta::YAML version 0.020'
JSON-Validator-5.18/lib/ 0000755 0000765 0000024 00000000000 15211411467 014602 5 ustar jhthorsen staff JSON-Validator-5.18/lib/JSON/ 0000755 0000765 0000024 00000000000 15211411467 015353 5 ustar jhthorsen staff JSON-Validator-5.18/lib/JSON/Validator.pm 0000644 0000765 0000024 00000034210 15211411456 017634 0 ustar jhthorsen staff package JSON::Validator;
use Mojo::Base -base;
use Carp qw(confess);
use JSON::Validator::Store;
use JSON::Validator::Util qw(E data_checksum is_type);
use Mojo::Util qw(sha1_sum);
use Scalar::Util qw(blessed);
our $VERSION = '5.18';
our %SCHEMAS = (
'http://json-schema.org/draft-04/schema#' => '+Draft4',
'http://json-schema.org/draft-06/schema#' => '+Draft6',
'http://json-schema.org/draft-07/schema#' => '+Draft7',
'https://json-schema.org/draft/2019-09/schema' => '+Draft201909',
'http://swagger.io/v2/schema.json' => '+OpenAPIv2',
'https://spec.openapis.org/oas/3.0/schema/2021-09-28' => '+OpenAPIv3',
'https://spec.openapis.org/oas/3.1/schema/2021-05-20' => '+OpenAPIv3',
);
has formats => sub { require JSON::Validator::Schema; JSON::Validator::Schema->_build_formats };
has recursive_data_protection => 1;
has store => sub {
my $self = shift;
my %attrs;
$attrs{$_} = delete $self->{$_} for grep { $self->{$_} } qw(cache_paths ua);
return JSON::Validator::Store->new(%attrs);
};
# store proxy attributes
for my $method (qw(cache_paths ua)) {
Mojo::Util::monkey_patch(__PACKAGE__, $method => sub { shift->store->$method(@_) });
}
sub bundle { shift->schema->bundle(@_)->data }
sub coerce {
my $self = shift;
return $self->{coerce} ||= {} unless defined(my $what = shift);
state $short = {bool => 'booleans', def => 'defaults', num => 'numbers', str => 'strings'};
$what = {map { ($_ => 1) } split /,/, $what} unless ref $what;
$self->{coerce} = {};
$self->{coerce}{($short->{$_} || $_)} = $what->{$_} for keys %$what;
return $self;
}
sub get { shift->schema->get(@_) }
sub load_and_validate_schema {
my ($self, $schema, $args) = @_;
delete $self->{schema};
my $schema_obj = $self->_new_schema($schema, %$args);
confess join "\n", "Invalid JSON specification", (ref $schema eq 'HASH' ? Mojo::Util::dumper($schema) : $schema),
map {"- $_"} @{$schema_obj->errors}
if @{$schema_obj->errors};
$self->{schema} = $schema_obj;
return $self;
}
sub new {
my $self = shift->SUPER::new(@_);
$self->coerce($self->{coerce}) if defined $self->{coerce};
return $self;
}
sub schema {
my $self = shift;
return $self->{schema} //= $self->_new_schema({}) unless @_;
$self->{schema} = $self->_new_schema(shift);
return $self;
}
sub validate {
my ($self, $data, $schema) = @_;
return +(defined $schema ? $self->_new_schema($schema) : $self->schema)->validate($_[1]);
}
sub _new_schema {
my ($self, $source, %attrs) = @_;
return $source if blessed $source and $source->can('specification');
# Compat with load_and_validate_schema()
$attrs{specification} = delete $attrs{schema} if $attrs{schema};
my $loadable
= (blessed $source && ($source->can('scheme') || $source->isa('Mojo::File')))
|| ($source !~ /\n/ && -f $source)
|| (!ref $source && $source =~ /^\w/);
my $store = $self->store;
my $schema = $loadable ? $store->get($store->load($source)) : $source;
$attrs{recursive_data_protection} //= $self->recursive_data_protection;
$attrs{coerce} ||= $self->{coerce} if $self->{coerce};
$attrs{formats} ||= $self->{formats} if $self->{formats};
$attrs{specification} = $schema->{'$schema'}
if !$attrs{specification}
and is_type $schema, 'HASH'
and $schema->{'$schema'};
$attrs{store} = $store;
# Detect openapiv2 and v3 schemas by content, since no "$schema" is present
my $spec = $attrs{specification} || $schema;
if (ref $spec eq 'HASH' and $spec->{paths}) {
if ($spec->{swagger} and $spec->{swagger} eq '2.0') {
$spec = 'http://swagger.io/v2/schema.json';
}
elsif ($spec->{openapi} and $spec->{openapi} =~ m!^3\.1\.\d+$!) {
$spec = 'https://spec.openapis.org/oas/3.1/schema/2021-05-20';
$attrs{specification} ||= $spec;
}
elsif ($spec->{openapi} and $spec->{openapi} =~ m!^3\.0\.\d+$!) {
$spec = 'https://spec.openapis.org/oas/3.0/schema/2021-09-28';
}
}
my $schema_class = $spec && $SCHEMAS{$spec} || 'JSON::Validator::Schema';
$schema_class =~ s!^\+(.+)$!JSON::Validator::Schema::$1!;
confess "Could not load $schema_class: $@" unless $schema_class->can('new') or eval "require $schema_class;1";
return $schema_class->new($source, %attrs);
}
1;
=encoding utf8
=head1 NAME
JSON::Validator - Validate data against a JSON schema
=head1 SYNOPSIS
=head2 Using a schema object
L or any of the sub classes can be used instead of
L. The only reason to use L directly is if
you don't know the schema version up front.
=head2 Basics
use JSON::Validator;
my $jv = JSON::Validator->new;
# Define a schema - http://json-schema.org/learn/miscellaneous-examples.html
# You can also load schema from disk or web
$jv->schema({
type => "object",
required => ["firstName", "lastName"],
properties => {
firstName => {type => "string"},
lastName => {type => "string"},
age => {type => "integer", minimum => 0, description => "Age in years"}
}
});
# Validate your data
my @errors = $jv->validate({firstName => "Jan Henning", lastName => "Thorsen", age => -42});
# Do something if any errors was found
die "@errors" if @errors;
=head2 Using joi
# Use joi() to build the schema
use JSON::Validator::Joi 'joi';
$jv->schema(joi->object->props({
firstName => joi->string->required,
lastName => joi->string->required,
age => joi->integer->min(0),
}));
# joi() can also validate directly
my @errors = joi(
{firstName => "Jan Henning", lastName => "Thorsen", age => -42},
joi->object->props({
firstName => joi->string->required,
lastName => joi->string->required,
age => joi->integer->min(0),
}),
);
=head1 DESCRIPTION
L is a data structure validation library based around
L. This module can be used directly with
a JSON schema or you can use the elegant DSL schema-builder
L to define the schema programmatically.
=head2 Supported schema formats
L can load JSON schemas in multiple formats: Plain perl data
structured (as shown in L), JSON or YAML. The JSON parsing is done
with L, while YAML files requires L or L.
=head2 Resources
Here are some resources that are related to JSON schemas and validation:
=over 4
=item * L
=item * L
=item * L
=back
=head2 Bundled specifications
This module comes with some JSON specifications bundled, so your application
don't have to fetch those from the web. These specifications should be up to
date, but please submit an issue if they are not.
Files referenced to an URL will automatically be cached if the first element in
L is a writable directory. Note that the cache headers for the
remote assets are B honored, so you will manually need to remove any
cached file, should you need to refresh them.
To download and cache an online asset, do this:
JSON_VALIDATOR_CACHE_PATH=/some/writable/directory perl myapp.pl
Here is the list of the bundled specifications:
=over 2
=item * JSON schema, draft 4, 6, 7, 2019-09.
Web page: L
C<$ref>: L,
L,
L.
=item * JSON schema for JSONPatch files
Web page: L
C<$ref>: L
=item * Swagger / OpenAPI specification, version 2
Web page: L
C<$ref>: L
=item * OpenAPI specification, version 3
Web page: L
C<$ref>: L
This specification is still EXPERIMENTAL.
=item * Swagger Petstore
This is used for unit tests, and should not be relied on by external users.
=back
=head2 Optional modules
=over 2
=item * Sereal::Encoder
Installing L v4.00 (or later) will make
L significantly faster. This function is
used both when parsing schemas and validating data.
=item * Format validators
See the documentation in L for other optional modules
to do validation of specific "format", such as "hostname", "ipv4" and others.
=back
=head1 ATTRIBUTES
=head2 cache_paths
Proxy attribute for L.
=head2 formats
This attribute will be used as default value for
L. It is highly recommended to change this
directly on the L instead:
$jv->formats(...); # Legacy
$jv->schema->formats(...); # Recommended way
=head2 recursive_data_protection
This attribute will be used as default value for
L. It is highly recommended
to change this directly on the L instead:
$jv->recursive_data_protection(...); # Legacy
$jv->schema->recursive_data_protection(...); # Recommended way
=head2 store
$store = $jv->store;
Holds a L object that caches the retrieved schemas.
This object will be shared amongst different L objects to prevent
a schema from having to be downloaded again.
=head2 ua
Proxy attribute for L.
=head1 METHODS
=head2 bundle
This method can be used to get a bundled version of L. It will however
return a data-structure instead of a new object. See
L for an alternative.
# These two lines does the same
$data = $jv->bundle;
$data = $jv->schema->bundle->data;
# Recommended way
$schema = $jv->schema->bundle;
=head2 coerce
This attribute will be used as default value for
L. It is highly recommended to change this
directly on the L instead:
$jv->coerce(...); # Legacy
$jv->schema->coerce(...); # Recommended way
=head2 get
Proxy method for L.
=head2 new
$jv = JSON::Validator->new(%attributes);
$jv = JSON::Validator->new(\%attributes);
Creates a new L object.
=head2 load_and_validate_schema
This method will be deprecated in the future. See
L and L
instead.
=head2 schema
$jv = $jv->schema($json_or_yaml_string);
$jv = $jv->schema($url);
$jv = $jv->schema(\%schema);
$jv = $jv->schema(JSON::Validator::Joi->new);
$jv = $jv->schema(JSON::Validator::Schema->new);
$schema = $jv->schema;
Used to set a schema from either a data structure or a URL.
C<$schema> will be an instance of L,
L L,
L, L,
L or L.
The C<$url> can take many forms, but needs to point to a text file in the
JSON or YAML format.
=over 4
=item * file://...
A file on disk. Note that it is required to use the "file" scheme if you want
to reference absolute paths on your file system.
=item * http://... or https://...
A web resource will be fetched using the L, stored in L.
=item * data://Some::Module/spec.json
Will load a given "spec.json" file from C using
L.
=item * data:///spec.json
A "data" URL without a module name will use the current package and search up
the call/inheritance tree.
=item * Any other URL
An URL (without a recognized scheme) will be treated as a path to a file on
disk. If the file could not be found on disk and the path starts with "/", then
the will be loaded from the app defined in L. Something like this:
$jv->ua->server->app(MyMojoApp->new);
$jv->ua->get('/any/other/url.json');
=back
=head2 validate
Proxy method for L.
=head1 SEE ALSO
=over 2
=item * L
L contains utility functions for validating data
types. Could be useful for validating data without loading a schema.
=item * L
L is the base class for
L, L
L, L,
L or L.
=item * L
L contains many useful function when working with
schemas.
=item * L
L is a plugin for L that utilize
L and the L
to build routes with input and output validation.
=back
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2014-2021, Jan Henning Thorsen
This program is free software, you can redistribute it and/or modify it under
the terms of the Artistic License version 2.0.
=head1 AUTHORS
=head2 Project Founder
Jan Henning Thorsen - C
=head2 Contributors
=over 2
=item * Aleksandr Orlenko
=item * Alexander Hartmaier
=item * Alexander Karelas
=item * Bernhard Graf
=item * Brad Barden
=item * Dagfinn Ilmari Mannsåker
=item * Daniel Böhmer
=item * David Cantrell
=item * Ed J
=item * Ere Maijala
=item * Fabrizio Gennari
=item * Ilya Rassadin
=item * Jason Cooper
=item * Karen Etheridge
=item * Kenichi Ishigaki
=item * Kevin M. Goess
=item * Kirill Matusov
=item * Krasimir Berov
=item * Lari Taskula
=item * Lee Johnson
=item * Martin Renvoize
=item * Mattias Päivärinta
=item * Michael Jemmeson
=item * Michael Schout
=item * Mohammad S Anwar
=item * Nick Morrott
=item * Pierre-Aymeric Masse
=item * Roy Storey
=item * Russell Jenkins
=item * Sebastian Riedel
=item * Stephan Hradek
=item * Tim Stallard
=item * Zoffix Znet
=back
=cut
JSON-Validator-5.18/lib/JSON/Validator/ 0000755 0000765 0000024 00000000000 15211411467 017300 5 ustar jhthorsen staff JSON-Validator-5.18/lib/JSON/Validator/Store.pm 0000644 0000765 0000024 00000022723 15211411456 020736 0 ustar jhthorsen staff package JSON::Validator::Store;
use Mojo::Base -base;
use Mojo::Exception;
use Mojo::File qw(path);
use Mojo::JSON;
use Mojo::JSON::Pointer;
use Mojo::UserAgent;
use Mojo::Util qw(url_unescape);
use JSON::Validator::Schema;
use JSON::Validator::URI qw(uri);
use JSON::Validator::Util qw(data_section str2data);
use Scalar::Util qw(blessed);
use constant DEBUG => $ENV{JSON_VALIDATOR_DEBUG} && 1;
use constant BUNDLED_PATH => path(path(__FILE__)->dirname, 'cache')->to_string;
use constant CASE_TOLERANT => File::Spec->case_tolerant;
die $@ unless eval q(package JSON::Validator::Exception; use Mojo::Base 'Mojo::Exception'; 1);
has cache_paths => sub { [split(/:/, $ENV{JSON_VALIDATOR_CACHE_PATH} || ''), BUNDLED_PATH] };
has schemas => sub { +{} };
has ua => sub {
my $ua = Mojo::UserAgent->new;
$ua->proxy->detect;
return $ua->max_redirects(3);
};
sub add {
my ($self, $id, $schema) = @_;
$id =~ s!(.)#$!$1!;
$self->schemas->{$id} = $schema;
return $id;
}
sub exists {
my ($self, $id) = @_;
return undef unless defined $id;
$id =~ s!(.)#$!$1!;
return $self->schemas->{$id} && $id;
}
sub get {
my ($self, $id) = @_;
return undef unless defined $id;
$id =~ s!(.)#$!$1!;
return $self->schemas->{$id};
}
sub load {
return
$_[0]->_load_from_url($_[1])
|| $_[0]->_load_from_data($_[1])
|| $_[0]->_load_from_text($_[1])
|| $_[0]->_load_from_file($_[1])
|| $_[0]->_load_from_app($_[1])
|| $_[0]->get($_[1])
|| _raise(qq(Unable to load schema "$_[1]".));
}
sub resolve {
my ($self, $ref, $curr) = @_;
$curr //= {base_url => ''};
my ($base_url, $fragment) = split '#', $ref;
my $abs_url = uri($base_url)->fragment($fragment);
$abs_url = uri $abs_url, $curr->{base_url} if $curr->{base_url} and !$abs_url->is_abs;
$fragment = '' unless defined $fragment;
$base_url ||= $curr->{base_url} || '';
warn "[JSON::Validator] Resolve curr: ref=$ref,@{[map qq($_=$curr->{$_}), sort keys %$curr]}\n" if DEBUG;
my $state = {base_url => $base_url, fragment => $fragment, source => 'unknown'};
if (defined(my $schema = $self->schemas->{$abs_url})) {
@$state{qw(base_url id root schema source)} = ($abs_url, $abs_url, $schema, $schema, 'schema/abs_url');
}
elsif (defined(my $root = $self->schemas->{$base_url})) {
@$state{qw(base_url id root source)} = ($base_url, $base_url, $root, 'schema/base_url');
}
elsif ($base_url) {
$base_url = uri $base_url, $curr->{base_url} if $curr->{base_url};
my $id = $self->load($base_url);
@$state{qw(base_url id root source)} = ($id, $id, $self->get($id), 'load');
$state->{root} = $self->get($id);
}
else {
@$state{qw(id root source)} = ('', $curr->{root}, 'root');
}
$fragment =~ s!%2f!~1!; # /
$fragment =~ s!%7e!~0!; # ~
$fragment = url_unescape $fragment;
$state->{schema} //= length $fragment ? Mojo::JSON::Pointer->new($state->{root})->get($fragment) : $state->{root};
_raise(qq[Unable to resolve "$ref" from "$state->{base_url}". ($state->{source})]) unless defined $state->{schema};
$state->{$_} //= $curr->{$_} for keys %$curr; # pass on original information
warn "[JSON::Validator] Resolve state: @{[map qq($_=$state->{$_}), sort keys %$state]}\n" if DEBUG;
return $state;
}
sub _add {
my ($self, $id, $schema) = @_;
$id = $self->add($id => $schema);
if (ref $schema eq 'HASH') {
return
$schema->{'$id'} ? $self->add($schema->{'$id'} => $schema)
: $schema->{id} ? $self->add($schema->{id} => $schema)
: $id;
}
return $id;
}
sub _load_from_app {
return undef unless $_[1] =~ m!^/!;
my ($self, $url) = @_;
my $id;
return undef unless $self->ua->server->app;
return undef if blessed $url and !$url->can('scheme');
return $id if $id = $self->exists($url);
my $tx = $self->ua->get($url);
my $err = $tx->error && $tx->error->{message};
_raise("GET $url: $err") if $err;
warn "[JSON::Validator] Load from app $url\n" if DEBUG;
return $self->_add($url => str2data $tx->res->body);
}
sub _load_from_data {
return undef unless $_[1] =~ m!^data://([^/]*)/(.*)!;
my ($self, $url) = @_;
my $id;
return $id if $id = $self->exists($url);
my ($class, $file) = ($1, $2); # data://([^/]*)/(.*)
my $text = data_section $class, $file, {encoding => 'UTF-8'};
_raise("Could not find $url") unless $text;
warn "[JSON::Validator] Load from data $file in $class\n" if DEBUG;
return $self->_add($url => str2data $text);
}
sub _load_from_file {
my ($self, $file) = @_;
$file =~ s!^file://!!;
$file =~ s!#$!!;
$file = path(split '/', url_unescape $file);
return undef unless -e $file;
$file = $file->realpath;
my $id = uri()->new->scheme('file')->host('')->path(CASE_TOLERANT ? lc $file : "$file");
warn "[JSON::Validator] Load from file $file\n" if DEBUG;
return $self->exists($id) || $self->_add($id => str2data $file->slurp);
}
sub _load_from_text {
my ($self, $text) = @_;
my $is_scalar_ref = ref $text eq 'SCALAR';
return undef unless $is_scalar_ref or $text =~ m!^\s*(?:---|\{)!s;
my $id = uri->from_data($is_scalar_ref ? $$text : $text);
warn "[JSON::Validator] Load from text $id\n" if DEBUG;
return $self->exists($id) || $self->_add($id => str2data $is_scalar_ref ? $$text : $text);
}
sub _load_from_url {
return undef unless $_[1] =~ m!^https?://!;
my ($self, $url) = @_;
my $id;
return $id if $id = $self->exists($url);
$url = uri($url)->fragment(undef);
return $id if $id = $self->exists($url);
my $cache_path = $self->cache_paths->[0];
my $cache_file = Mojo::Util::md5_sum("$url");
for (@{$self->cache_paths}) {
my $path = path $_, $cache_file;
warn "[JSON::Validator] Load from cache $path\n" if DEBUG and -r $path;
return $self->_add($url => str2data $path->slurp) if -r $path;
}
my $tx = $self->ua->get($url);
my $err = $tx->error && $tx->error->{message};
_raise("GET $url: $err") if $err;
if ($cache_path and $cache_path ne BUNDLED_PATH and -w $cache_path) {
$cache_file = path $cache_path, $cache_file;
$cache_file->spurt($tx->res->body);
}
warn "[JSON::Validator] Load from URL $url\n" if DEBUG;
return $self->_add($url => str2data $tx->res->body);
}
sub _raise { die JSON::Validator::Exception->new(@_)->trace }
1;
=encoding utf8
=head1 NAME
JSON::Validator::Store - Load and caching JSON schemas
=head1 SYNOPSIS
use JSON::Validator;
my $jv = JSON::Validator->new;
$jv->store->add("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f" => {...});
$jv->store->load("http://api.example.com/my/schema.json");
=head1 DESCRIPTION
L is a class for loading and caching JSON-Schemas.
=head1 ATTRIBUTES
=head2 cache_paths
my $store = $store->cache_paths(\@paths);
my $array_ref = $store->cache_paths;
A list of directories to where cached specifications are stored. Defaults to
C environment variable and the specs that is bundled
with this distribution.
C can be a list of directories, each separated by ":".
See L for more details.
=head2 schemas
my $hash_ref = $store->schemas;
my $store = $store->schemas({});
Hold the schemas as data structures. The keys are schema "id".
=head2 ua
my $ua = $store->ua;
my $store = $store->ua(Mojo::UserAgent->new);
Holds a L object, used by L to load a JSON schema
from remote location.
The default L will detect proxy settings and have
L set to 3.
=head1 METHODS
=head2 add
my $normalized_id = $store->add($id => \%schema);
Used to add a schema data structure. Note that C<$id> might not be the same as
C<$normalized_id>.
=head2 exists
my $normalized_id = $store->exists($id);
Returns a C<$normalized_id> if it is present in the L.
=head2 get
my $schema = $store->get($normalized_id);
Used to retrieve a C<$schema> added by L or L.
=head2 load
my $normalized_id = $store->load('https://...');
my $normalized_id = $store->load('data://main/foo.json');
my $normalized_id = $store->load('---\nid: yaml');
my $normalized_id = $store->load('{"id":"yaml"}');
my $normalized_id = $store->load(\$text);
my $normalized_id = $store->load('/path/to/foo.json');
my $normalized_id = $store->load('file:///path/to/foo.json');
my $normalized_id = $store->load('/load/from/ua-server-app');
Can load a C<$schema> from many different sources. The input can be a string or
a string-like object, and the L method will try to resolve it in the
order listed in above.
Loading schemas from C<$text> will generate an C<$normalized_id> in L
looking like "urn:text:$text_checksum". This might change in the future!
Loading files from disk will result in a C<$normalized_id> that always start
with "file://".
Loading can also be done with relative path, which will then load from:
$store->ua->server->app;
This method is EXPERIMENTAL, but unlikely to change significantly.
=head2 resolve
$hash_ref = $store->resolve($url, \%defaults);
Takes a C<$url> (can also be a file, urn, ...) with or without a fragment and
returns this structure about the schema:
{
base_url => $str, # the part before the fragment in the $url
fragment => $str, # fragment part of the $url
id => $str, # store ID
root => ..., # the root schema
schema => ..., # the schema inside "root" if fragment is present
}
This method is EXPERIMENTAL and can change without warning.
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Joi.pm 0000644 0000765 0000024 00000024725 15211411456 020367 0 ustar jhthorsen staff package JSON::Validator::Joi;
use Mojo::Base -base;
use Exporter 'import';
use List::Util qw(uniq);
use Mojo::JSON qw(false true);
use Mojo::Util;
use Scalar::Util qw(blessed);
use Storable qw(dclone);
our @EXPORT_OK = qw(joi);
# Avoid "Subroutine redefined" warnings
require JSON::Validator::Schema::Draft7;
has enum => sub { +[] };
has type => 'object';
has validator => sub { JSON::Validator::Schema::Draft7->new->coerce('booleans,numbers,strings') };
has [qw(format max min multiple_of regex)];
for my $attr (qw(required strict unique)) {
Mojo::Util::monkey_patch(__PACKAGE__, $attr => sub { $_[0]->{$attr} = $_[1] // 1; $_[0]; });
}
sub alphanum { shift->_type('string')->regex('^\w*$') }
sub boolean { shift->type('boolean') }
sub compile {
my $self = shift;
my $merged = {};
for (ref $self->type eq 'ARRAY' ? @{$self->type} : $self->type) {
my $method = "_compile_$_";
my $compiled = $self->$method;
@$merged{keys %$compiled} = values %$compiled;
}
return $merged;
}
sub date_time { shift->_type('string')->format('date-time') }
sub email { shift->_type('string')->format('email') }
sub extend {
my ($self, $by) = @_;
die "Cannot extend joi '@{[$self->type]}' by '@{[$by->type]}'" unless $self->type eq $by->type;
my $clone = shift->new(dclone($self));
for my $key (keys %$by) {
my $ref = ref $by->{$key};
$clone->{$key} = $by->{$key} unless $ref eq 'ARRAY' or $ref eq 'HASH';
}
if ($self->type eq 'array') {
$clone->{items} = dclone($by->{items}) if $by->{items};
}
elsif ($self->type eq 'object') {
$clone->{required_props} = [uniq @{$clone->{required_props}}, @{$by->{required_props}}]
if defined $by->{required_props};
$clone->{properties}{$_} = dclone($by->{properties}{$_}) for keys %{$by->{properties} || {}};
}
return $clone;
}
sub array { shift->type('array') }
sub integer { shift->type('integer') }
sub iso_date { shift->date_time }
sub items { $_[0]->{items} = $_[1]; $_[0] }
sub joi { __PACKAGE__->new(@_) }
sub length { shift->min($_[0])->max($_[0]) }
sub lowercase { shift->_type('string')->regex('^\p{Lowercase}*$') }
sub negative { shift->_type('number')->max(0) }
sub number { shift->type('number') }
sub object { shift->type('object') }
sub pattern { shift->regex(@_) }
sub positive { shift->number->min(0) }
sub props {
my $self = shift->type('object');
my %properties = ref $_[0] ? %{$_[0]} : @_;
while (my ($name, $property) = each %properties) {
push @{$self->{required_props}}, $name if $property->{required};
$self->{properties}{$name} = $property->compile;
}
return $self;
}
sub string { shift->type('string') }
sub token { shift->_type('string')->regex('^[a-zA-Z0-9_]+$') }
sub uppercase { shift->_type('string')->regex('^\p{Uppercase}*$') }
sub uri { shift->_type('string')->format('uri') }
sub validate {
my ($self, $data) = @_;
return $self->validator->data($self->compile)->validate($data);
}
sub _compile_array {
my $self = shift;
my $json = {type => $self->type};
$json->{additionalItems} = false if $self->{strict};
$json->{maxItems} = $self->{max} if defined $self->{max};
$json->{minItems} = $self->{min} if defined $self->{min};
$json->{uniqueItems} = true if $self->{unique};
if ($self->{items}) {
$json->{items} = $self->{items};
for my $item (ref $json->{items} eq 'ARRAY' ? @{$json->{items}} : $json->{items}) {
$item = $item->compile if blessed $item and $item->can('compile');
}
}
return $json;
}
sub _compile_boolean { +{type => 'boolean'} }
sub _compile_integer { shift->_compile_number }
sub _compile_null { {type => shift->type} }
sub _compile_number {
my $self = shift;
my $json = {type => $self->type};
$json->{enum} = $self->{enum} if defined $self->{enum} and @{$self->{enum}};
$json->{maximum} = $self->{max} if defined $self->{max};
$json->{minimum} = $self->{min} if defined $self->{min};
$json->{multipleOf} = $self->{multiple_of} if defined $self->{multiple_of};
return $json;
}
sub _compile_object {
my $self = shift;
my $json = {type => $self->type};
$json->{additionalProperties} = false if $self->{strict};
$json->{maxProperties} = $self->{max} if defined $self->{max};
$json->{minProperties} = $self->{min} if defined $self->{min};
$json->{patternProperties} = $self->{regex} if $self->{regex};
$json->{properties} = $self->{properties} if ref $self->{properties} eq 'HASH';
$json->{required} = $self->{required_props} if defined $self->{required_props};
return $json;
}
sub _compile_string {
my $self = shift;
my $json = {type => $self->type};
$json->{enum} = $self->{enum} if defined $self->{enum} and @{$self->{enum}};
$json->{format} = $self->{format} if defined $self->{format};
$json->{maxLength} = $self->{max} if defined $self->{max};
$json->{minLength} = $self->{min} if defined $self->{min};
$json->{pattern} = $self->{regex} if defined $self->{regex};
return $json;
}
sub _type {
$_[0]->{type} = $_[1] unless $_[0]->{type};
return $_[0];
}
sub TO_JSON { shift->compile }
1;
=encoding utf8
=head1 NAME
JSON::Validator::Joi - Joi validation sugar for JSON::Validator
=head1 SYNOPSIS
use JSON::Validator::Joi "joi";
my @errors = joi->object->props(
age => joi->integer->min(0)->max(200),
email => joi->regex(".@.")->required,
name => joi->string->min(1),
)->validate({
name => "Jan Henning",
age => 34,
email => "jhthorsen@cpan.org",
});
die "@errors" if @errors;
=head2 EXPORTED FUNCTIONS
=head2 joi
$joi = joi(%attrs);
Same as:
JSON::Validator::Joi->new(%attrs);
=head1 DESCRIPTION
L is an elegant DSL schema-builder. The main purpose is
to build a L for L, but
it can also validate data directly with sane defaults.
=head1 ATTRIBUTES
=head2 enum
my $joi = $joi->enum(["foo", "bar"]);
my $array_ref = $joi->enum;
Defines a list of enum values for L, L and L.
=head2 format
my $joi = $joi->format("email");
my $str = $joi->format;
Used to set the format of the L.
See also L, L and L.
=head2 max
my $joi = $joi->max(10);
my $int = $joi->max;
=over 2
=item * array
Defines the max number of items in the array.
=item * integer, number
Defined the max value.
=item * object
Defines the max number of items in the object.
=item * string
Defines how long the string can be.
=back
=head2 min
my $joi = $joi->min(10);
my $int = $joi->min;
=over 2
=item * array
Defines the minimum number of items in the array.
=item * integer, number
Defined the minimum value.
=item * object
Defines the minimum number of items in the object.
=item * string
Defines how short the string can be.
=back
=head2 multiple_of
my $joi = $joi->multiple_of(3);
my $int = $joi->multiple_of;
Used by L and L to define what the number must be a multiple
of.
=head2 regex
my $joi = $joi->regex("^\w+$");
my $str = $joi->regex;
Defines a pattern that L will be validated against.
=head2 type
my $joi = $joi->type("string");
my $joi = $joi->type([qw(null integer)]);
my $any = $joi->type;
Sets the required type. This attribute is set by the convenience methods
L, L, L and L, but can be set manually if
you need to check against a list of type.
=head2 validator
my $joi = $joi->validator(JSON::Validator::Schema::Draft7->new);
my $jv = $joi->validator;
Defaults to a L object. This object is used by L.
Note: This might change to L or a later
schema in the future.
=head1 METHODS
=head2 TO_JSON
Alias for L.
=head2 alphanum
my $joi = $joi->alphanum;
Sets L to "^\w*$".
=head2 array
my $joi = $joi->array;
Sets L to "array".
=head2 boolean
my $joi = $joi->boolean;
Sets L to "boolean".
=head2 compile
my $hash_ref = $joi->compile;
Will convert this object into a JSON-Schema data structure that
L understands.
=head2 date_time
my $joi = $joi->date_time;
Sets L to L.
=head2 email
my $joi = $joi->email;
Sets L to L.
=head2 extend
my $new_joi = $joi->extend($other_joi_object);
Will extend C<$joi> with the definitions in C<$other_joi_object> and return a
new object.
=head2 iso_date
Alias for L.
=head2 integer
my $joi = $joi->integer;
Sets L to "integer".
=head2 items
my $joi = $joi->items($joi);
my $joi = $joi->items([$joi, ...]);
Defines a list of items for the L type.
=head2 length
my $joi = $joi->length(10);
Sets both L and L to the number provided.
=head2 lowercase
my $joi = $joi->lowercase;
Will set L to only match lower case strings.
=head2 negative
my $joi = $joi->negative;
Sets L to C<0>.
=head2 number
my $joi = $joi->number;
Sets L to "number".
=head2 object
my $joi = $joi->object;
Sets L to "object".
=head2 pattern
Alias for L.
=head2 positive
my $joi = $joi->positive;
Sets L to C<0>.
=head2 props
my $joi = $joi->props(name => JSON::Validator::Joi->new->string, ...);
Used to define properties for an L type. Each key is the name of the
parameter and the values must be a L object.
=head2 required
my $joi = $joi->required;
Marks the current property as required.
=head2 strict
my $joi = $joi->strict;
Sets L and L to not allow any more items/keys than what is defined.
=head2 string
my $joi = $joi->string;
Sets L to "string".
=head2 token
my $joi = $joi->token;
Sets L to C<^[a-zA-Z0-9_]+$>.
=head2 validate
my @errors = $joi->validate($data);
Used to validate C<$data> using L. Returns a list of
L objects on invalid
input.
=head2 unique
my $joi = $joi->unique;
Used to force the L to only contain unique items.
=head2 uppercase
my $joi = $joi->uppercase;
Will set L to only match upper case strings.
=head2 uri
my $joi = $joi->uri;
Sets L to L.
=head1 SEE ALSO
L
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/cache/ 0000755 0000765 0000024 00000000000 15211411467 020343 5 ustar jhthorsen staff JSON-Validator-5.18/lib/JSON/Validator/cache/3d35aac549d951f4cf9182ff47bff0b4 0000644 0000765 0000024 00000010535 15210277065 025027 0 ustar jhthorsen staff {
"$schema": "http://json-schema.org/draft-06/schema#",
"$id": "http://json-schema.org/draft-06/schema#",
"title": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#" }
},
"nonNegativeInteger": {
"type": "integer",
"minimum": 0
},
"nonNegativeIntegerDefault0": {
"allOf": [
{ "$ref": "#/definitions/nonNegativeInteger" },
{ "default": 0 }
]
},
"simpleTypes": {
"enum": [
"array",
"boolean",
"integer",
"null",
"number",
"object",
"string"
]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true,
"default": []
}
},
"type": ["object", "boolean"],
"properties": {
"$id": {
"type": "string",
"format": "uri-reference"
},
"$schema": {
"type": "string",
"format": "uri"
},
"$ref": {
"type": "string",
"format": "uri-reference"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": {},
"examples": {
"type": "array",
"items": {}
},
"multipleOf": {
"type": "number",
"exclusiveMinimum": 0
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "number"
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "number"
},
"maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
"minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": { "$ref": "#" },
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
},
"maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
"minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"contains": { "$ref": "#" },
"maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
"minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"required": { "$ref": "#/definitions/stringArray" },
"additionalProperties": { "$ref": "#" },
"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" }
]
}
},
"propertyNames": { "$ref": "#" },
"const": {},
"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": "#" }
},
"default": {}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/36d1bd12eeed51e86c8695bd8876a9df 0000644 0000765 0000024 00000116470 15210277065 025052 0 ustar jhthorsen staff {
"title": "A JSON Schema for Swagger 2.0 API.",
"id": "http://swagger.io/v2/schema.json#",
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": [
"swagger",
"info",
"paths"
],
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"swagger": {
"type": "string",
"enum": [
"2.0"
],
"description": "The Swagger version of this document."
},
"info": {
"$ref": "#/definitions/info"
},
"host": {
"type": "string",
"pattern": "^[^{}/ :\\\\]+(?::\\d+)?$",
"description": "The host (name or ip) of the API. Example: 'swagger.io'"
},
"basePath": {
"type": "string",
"pattern": "^/",
"description": "The base path to the API. Example: '/api'."
},
"schemes": {
"$ref": "#/definitions/schemesList"
},
"consumes": {
"description": "A list of MIME types accepted by the API.",
"allOf": [
{
"$ref": "#/definitions/mediaTypeList"
}
]
},
"produces": {
"description": "A list of MIME types the API can produce.",
"allOf": [
{
"$ref": "#/definitions/mediaTypeList"
}
]
},
"paths": {
"$ref": "#/definitions/paths"
},
"definitions": {
"$ref": "#/definitions/definitions"
},
"parameters": {
"$ref": "#/definitions/parameterDefinitions"
},
"responses": {
"$ref": "#/definitions/responseDefinitions"
},
"security": {
"$ref": "#/definitions/security"
},
"securityDefinitions": {
"$ref": "#/definitions/securityDefinitions"
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/tag"
},
"uniqueItems": true
},
"externalDocs": {
"$ref": "#/definitions/externalDocs"
}
},
"definitions": {
"info": {
"type": "object",
"description": "General information about the API.",
"required": [
"version",
"title"
],
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"title": {
"type": "string",
"description": "A unique and precise title of the API."
},
"version": {
"type": "string",
"description": "A semantic version number of the API."
},
"description": {
"type": "string",
"description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed."
},
"termsOfService": {
"type": "string",
"description": "The terms of service for the API."
},
"contact": {
"$ref": "#/definitions/contact"
},
"license": {
"$ref": "#/definitions/license"
}
}
},
"contact": {
"type": "object",
"description": "Contact information for the owners of the API.",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "The identifying name of the contact person/organization."
},
"url": {
"type": "string",
"description": "The URL pointing to the contact information.",
"format": "uri"
},
"email": {
"type": "string",
"description": "The email address of the contact person/organization.",
"format": "email"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"license": {
"type": "object",
"required": [
"name"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "The name of the license type. It's encouraged to use an OSI compatible license."
},
"url": {
"type": "string",
"description": "The URL pointing to the license.",
"format": "uri"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"paths": {
"type": "object",
"description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.",
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
},
"^/": {
"$ref": "#/definitions/pathItem"
}
},
"additionalProperties": false
},
"definitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/schema"
},
"description": "One or more JSON objects describing the schemas being consumed and produced by the API."
},
"parameterDefinitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/parameter"
},
"description": "One or more JSON representations for parameters"
},
"responseDefinitions": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/response"
},
"description": "One or more JSON representations for parameters"
},
"externalDocs": {
"type": "object",
"additionalProperties": false,
"description": "information about external documentation",
"required": [
"url"
],
"properties": {
"description": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"examples": {
"type": "object",
"additionalProperties": true
},
"mimeType": {
"type": "string",
"description": "The MIME type of the HTTP message."
},
"operation": {
"type": "object",
"required": [
"responses"
],
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
},
"summary": {
"type": "string",
"description": "A brief summary of the operation."
},
"description": {
"type": "string",
"description": "A longer description of the operation, GitHub Flavored Markdown is allowed."
},
"externalDocs": {
"$ref": "#/definitions/externalDocs"
},
"operationId": {
"type": "string",
"description": "A unique identifier of the operation."
},
"produces": {
"description": "A list of MIME types the API can produce.",
"allOf": [
{
"$ref": "#/definitions/mediaTypeList"
}
]
},
"consumes": {
"description": "A list of MIME types the API can consume.",
"allOf": [
{
"$ref": "#/definitions/mediaTypeList"
}
]
},
"parameters": {
"$ref": "#/definitions/parametersList"
},
"responses": {
"$ref": "#/definitions/responses"
},
"schemes": {
"$ref": "#/definitions/schemesList"
},
"deprecated": {
"type": "boolean",
"default": false
},
"security": {
"$ref": "#/definitions/security"
}
}
},
"pathItem": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"$ref": {
"type": "string"
},
"get": {
"$ref": "#/definitions/operation"
},
"put": {
"$ref": "#/definitions/operation"
},
"post": {
"$ref": "#/definitions/operation"
},
"delete": {
"$ref": "#/definitions/operation"
},
"options": {
"$ref": "#/definitions/operation"
},
"head": {
"$ref": "#/definitions/operation"
},
"patch": {
"$ref": "#/definitions/operation"
},
"parameters": {
"$ref": "#/definitions/parametersList"
}
}
},
"responses": {
"type": "object",
"description": "Response objects names can either be any valid HTTP status code or 'default'.",
"minProperties": 1,
"additionalProperties": false,
"patternProperties": {
"^([0-9]{3})$|^(default)$": {
"$ref": "#/definitions/responseValue"
},
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"not": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
}
},
"responseValue": {
"oneOf": [
{
"$ref": "#/definitions/response"
},
{
"$ref": "#/definitions/jsonReference"
}
]
},
"response": {
"type": "object",
"required": [
"description"
],
"properties": {
"description": {
"type": "string"
},
"schema": {
"oneOf": [
{
"$ref": "#/definitions/schema"
},
{
"$ref": "#/definitions/fileSchema"
}
]
},
"headers": {
"$ref": "#/definitions/headers"
},
"examples": {
"$ref": "#/definitions/examples"
}
},
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"headers": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/header"
}
},
"header": {
"type": "object",
"additionalProperties": false,
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"string",
"number",
"integer",
"boolean",
"array"
]
},
"format": {
"type": "string"
},
"items": {
"$ref": "#/definitions/primitivesItems"
},
"collectionFormat": {
"$ref": "#/definitions/collectionFormat"
},
"default": {
"$ref": "#/definitions/default"
},
"maximum": {
"$ref": "#/definitions/maximum"
},
"exclusiveMaximum": {
"$ref": "#/definitions/exclusiveMaximum"
},
"minimum": {
"$ref": "#/definitions/minimum"
},
"exclusiveMinimum": {
"$ref": "#/definitions/exclusiveMinimum"
},
"maxLength": {
"$ref": "#/definitions/maxLength"
},
"minLength": {
"$ref": "#/definitions/minLength"
},
"pattern": {
"$ref": "#/definitions/pattern"
},
"maxItems": {
"$ref": "#/definitions/maxItems"
},
"minItems": {
"$ref": "#/definitions/minItems"
},
"uniqueItems": {
"$ref": "#/definitions/uniqueItems"
},
"enum": {
"$ref": "#/definitions/enum"
},
"multipleOf": {
"$ref": "#/definitions/multipleOf"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"vendorExtension": {
"description": "Any property starting with x- is valid.",
"additionalProperties": true,
"additionalItems": true
},
"bodyParameter": {
"type": "object",
"required": [
"name",
"in",
"schema"
],
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"description": {
"type": "string",
"description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
},
"name": {
"type": "string",
"description": "The name of the parameter."
},
"in": {
"type": "string",
"description": "Determines the location of the parameter.",
"enum": [
"body"
]
},
"required": {
"type": "boolean",
"description": "Determines whether or not this parameter is required or optional.",
"default": false
},
"schema": {
"$ref": "#/definitions/schema"
}
},
"additionalProperties": false
},
"headerParameterSubSchema": {
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"required": {
"type": "boolean",
"description": "Determines whether or not this parameter is required or optional.",
"default": false
},
"in": {
"type": "string",
"description": "Determines the location of the parameter.",
"enum": [
"header"
]
},
"description": {
"type": "string",
"description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
},
"name": {
"type": "string",
"description": "The name of the parameter."
},
"type": {
"type": "string",
"enum": [
"string",
"number",
"boolean",
"integer",
"array"
]
},
"format": {
"type": "string"
},
"items": {
"$ref": "#/definitions/primitivesItems"
},
"collectionFormat": {
"$ref": "#/definitions/collectionFormat"
},
"default": {
"$ref": "#/definitions/default"
},
"maximum": {
"$ref": "#/definitions/maximum"
},
"exclusiveMaximum": {
"$ref": "#/definitions/exclusiveMaximum"
},
"minimum": {
"$ref": "#/definitions/minimum"
},
"exclusiveMinimum": {
"$ref": "#/definitions/exclusiveMinimum"
},
"maxLength": {
"$ref": "#/definitions/maxLength"
},
"minLength": {
"$ref": "#/definitions/minLength"
},
"pattern": {
"$ref": "#/definitions/pattern"
},
"maxItems": {
"$ref": "#/definitions/maxItems"
},
"minItems": {
"$ref": "#/definitions/minItems"
},
"uniqueItems": {
"$ref": "#/definitions/uniqueItems"
},
"enum": {
"$ref": "#/definitions/enum"
},
"multipleOf": {
"$ref": "#/definitions/multipleOf"
}
}
},
"queryParameterSubSchema": {
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"required": {
"type": "boolean",
"description": "Determines whether or not this parameter is required or optional.",
"default": false
},
"in": {
"type": "string",
"description": "Determines the location of the parameter.",
"enum": [
"query"
]
},
"description": {
"type": "string",
"description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
},
"name": {
"type": "string",
"description": "The name of the parameter."
},
"allowEmptyValue": {
"type": "boolean",
"default": false,
"description": "allows sending a parameter by name only or with an empty value."
},
"type": {
"type": "string",
"enum": [
"string",
"number",
"boolean",
"integer",
"array"
]
},
"format": {
"type": "string"
},
"items": {
"$ref": "#/definitions/primitivesItems"
},
"collectionFormat": {
"$ref": "#/definitions/collectionFormatWithMulti"
},
"default": {
"$ref": "#/definitions/default"
},
"maximum": {
"$ref": "#/definitions/maximum"
},
"exclusiveMaximum": {
"$ref": "#/definitions/exclusiveMaximum"
},
"minimum": {
"$ref": "#/definitions/minimum"
},
"exclusiveMinimum": {
"$ref": "#/definitions/exclusiveMinimum"
},
"maxLength": {
"$ref": "#/definitions/maxLength"
},
"minLength": {
"$ref": "#/definitions/minLength"
},
"pattern": {
"$ref": "#/definitions/pattern"
},
"maxItems": {
"$ref": "#/definitions/maxItems"
},
"minItems": {
"$ref": "#/definitions/minItems"
},
"uniqueItems": {
"$ref": "#/definitions/uniqueItems"
},
"enum": {
"$ref": "#/definitions/enum"
},
"multipleOf": {
"$ref": "#/definitions/multipleOf"
}
}
},
"formDataParameterSubSchema": {
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"required": {
"type": "boolean",
"description": "Determines whether or not this parameter is required or optional.",
"default": false
},
"in": {
"type": "string",
"description": "Determines the location of the parameter.",
"enum": [
"formData"
]
},
"description": {
"type": "string",
"description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
},
"name": {
"type": "string",
"description": "The name of the parameter."
},
"allowEmptyValue": {
"type": "boolean",
"default": false,
"description": "allows sending a parameter by name only or with an empty value."
},
"type": {
"type": "string",
"enum": [
"string",
"number",
"boolean",
"integer",
"array",
"file"
]
},
"format": {
"type": "string"
},
"items": {
"$ref": "#/definitions/primitivesItems"
},
"collectionFormat": {
"$ref": "#/definitions/collectionFormatWithMulti"
},
"default": {
"$ref": "#/definitions/default"
},
"maximum": {
"$ref": "#/definitions/maximum"
},
"exclusiveMaximum": {
"$ref": "#/definitions/exclusiveMaximum"
},
"minimum": {
"$ref": "#/definitions/minimum"
},
"exclusiveMinimum": {
"$ref": "#/definitions/exclusiveMinimum"
},
"maxLength": {
"$ref": "#/definitions/maxLength"
},
"minLength": {
"$ref": "#/definitions/minLength"
},
"pattern": {
"$ref": "#/definitions/pattern"
},
"maxItems": {
"$ref": "#/definitions/maxItems"
},
"minItems": {
"$ref": "#/definitions/minItems"
},
"uniqueItems": {
"$ref": "#/definitions/uniqueItems"
},
"enum": {
"$ref": "#/definitions/enum"
},
"multipleOf": {
"$ref": "#/definitions/multipleOf"
}
}
},
"pathParameterSubSchema": {
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"required": [
"required"
],
"properties": {
"required": {
"type": "boolean",
"enum": [
true
],
"description": "Determines whether or not this parameter is required or optional."
},
"in": {
"type": "string",
"description": "Determines the location of the parameter.",
"enum": [
"path"
]
},
"description": {
"type": "string",
"description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed."
},
"name": {
"type": "string",
"description": "The name of the parameter."
},
"type": {
"type": "string",
"enum": [
"string",
"number",
"boolean",
"integer",
"array"
]
},
"format": {
"type": "string"
},
"items": {
"$ref": "#/definitions/primitivesItems"
},
"collectionFormat": {
"$ref": "#/definitions/collectionFormat"
},
"default": {
"$ref": "#/definitions/default"
},
"maximum": {
"$ref": "#/definitions/maximum"
},
"exclusiveMaximum": {
"$ref": "#/definitions/exclusiveMaximum"
},
"minimum": {
"$ref": "#/definitions/minimum"
},
"exclusiveMinimum": {
"$ref": "#/definitions/exclusiveMinimum"
},
"maxLength": {
"$ref": "#/definitions/maxLength"
},
"minLength": {
"$ref": "#/definitions/minLength"
},
"pattern": {
"$ref": "#/definitions/pattern"
},
"maxItems": {
"$ref": "#/definitions/maxItems"
},
"minItems": {
"$ref": "#/definitions/minItems"
},
"uniqueItems": {
"$ref": "#/definitions/uniqueItems"
},
"enum": {
"$ref": "#/definitions/enum"
},
"multipleOf": {
"$ref": "#/definitions/multipleOf"
}
}
},
"nonBodyParameter": {
"type": "object",
"required": [
"name",
"in",
"type"
],
"oneOf": [
{
"$ref": "#/definitions/headerParameterSubSchema"
},
{
"$ref": "#/definitions/formDataParameterSubSchema"
},
{
"$ref": "#/definitions/queryParameterSubSchema"
},
{
"$ref": "#/definitions/pathParameterSubSchema"
}
]
},
"parameter": {
"oneOf": [
{
"$ref": "#/definitions/bodyParameter"
},
{
"$ref": "#/definitions/nonBodyParameter"
}
]
},
"schema": {
"type": "object",
"description": "A deterministic version of a JSON Schema object.",
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"$ref": {
"type": "string"
},
"format": {
"type": "string"
},
"title": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/title"
},
"description": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/description"
},
"default": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/default"
},
"multipleOf": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf"
},
"maximum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/maximum"
},
"exclusiveMaximum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum"
},
"minimum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/minimum"
},
"exclusiveMinimum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum"
},
"maxLength": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
},
"minLength": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
},
"pattern": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/pattern"
},
"maxItems": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
},
"minItems": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
},
"uniqueItems": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems"
},
"maxProperties": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
},
"minProperties": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
},
"required": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray"
},
"enum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/enum"
},
"additionalProperties": {
"anyOf": [
{
"$ref": "#/definitions/schema"
},
{
"type": "boolean"
}
],
"default": {}
},
"type": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/type"
},
"items": {
"anyOf": [
{
"$ref": "#/definitions/schema"
},
{
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/schema"
}
}
],
"default": {}
},
"allOf": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/schema"
}
},
"properties": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/schema"
},
"default": {}
},
"discriminator": {
"type": "string"
},
"readOnly": {
"type": "boolean",
"default": false
},
"xml": {
"$ref": "#/definitions/xml"
},
"externalDocs": {
"$ref": "#/definitions/externalDocs"
},
"example": {}
},
"additionalProperties": false
},
"fileSchema": {
"type": "object",
"description": "A deterministic version of a JSON Schema object.",
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"required": [
"type"
],
"properties": {
"format": {
"type": "string"
},
"title": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/title"
},
"description": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/description"
},
"default": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/default"
},
"required": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray"
},
"type": {
"type": "string",
"enum": [
"file"
]
},
"readOnly": {
"type": "boolean",
"default": false
},
"externalDocs": {
"$ref": "#/definitions/externalDocs"
},
"example": {}
},
"additionalProperties": false
},
"primitivesItems": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"enum": [
"string",
"number",
"integer",
"boolean",
"array"
]
},
"format": {
"type": "string"
},
"items": {
"$ref": "#/definitions/primitivesItems"
},
"collectionFormat": {
"$ref": "#/definitions/collectionFormat"
},
"default": {
"$ref": "#/definitions/default"
},
"maximum": {
"$ref": "#/definitions/maximum"
},
"exclusiveMaximum": {
"$ref": "#/definitions/exclusiveMaximum"
},
"minimum": {
"$ref": "#/definitions/minimum"
},
"exclusiveMinimum": {
"$ref": "#/definitions/exclusiveMinimum"
},
"maxLength": {
"$ref": "#/definitions/maxLength"
},
"minLength": {
"$ref": "#/definitions/minLength"
},
"pattern": {
"$ref": "#/definitions/pattern"
},
"maxItems": {
"$ref": "#/definitions/maxItems"
},
"minItems": {
"$ref": "#/definitions/minItems"
},
"uniqueItems": {
"$ref": "#/definitions/uniqueItems"
},
"enum": {
"$ref": "#/definitions/enum"
},
"multipleOf": {
"$ref": "#/definitions/multipleOf"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"security": {
"type": "array",
"items": {
"$ref": "#/definitions/securityRequirement"
},
"uniqueItems": true
},
"securityRequirement": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
},
"xml": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"namespace": {
"type": "string"
},
"prefix": {
"type": "string"
},
"attribute": {
"type": "boolean",
"default": false
},
"wrapped": {
"type": "boolean",
"default": false
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"tag": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/definitions/externalDocs"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"securityDefinitions": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/basicAuthenticationSecurity"
},
{
"$ref": "#/definitions/apiKeySecurity"
},
{
"$ref": "#/definitions/oauth2ImplicitSecurity"
},
{
"$ref": "#/definitions/oauth2PasswordSecurity"
},
{
"$ref": "#/definitions/oauth2ApplicationSecurity"
},
{
"$ref": "#/definitions/oauth2AccessCodeSecurity"
}
]
}
},
"basicAuthenticationSecurity": {
"type": "object",
"additionalProperties": false,
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"basic"
]
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"apiKeySecurity": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"name",
"in"
],
"properties": {
"type": {
"type": "string",
"enum": [
"apiKey"
]
},
"name": {
"type": "string"
},
"in": {
"type": "string",
"enum": [
"header",
"query"
]
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"oauth2ImplicitSecurity": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"flow",
"authorizationUrl"
],
"properties": {
"type": {
"type": "string",
"enum": [
"oauth2"
]
},
"flow": {
"type": "string",
"enum": [
"implicit"
]
},
"scopes": {
"$ref": "#/definitions/oauth2Scopes"
},
"authorizationUrl": {
"type": "string",
"format": "uri"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"oauth2PasswordSecurity": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"flow",
"tokenUrl"
],
"properties": {
"type": {
"type": "string",
"enum": [
"oauth2"
]
},
"flow": {
"type": "string",
"enum": [
"password"
]
},
"scopes": {
"$ref": "#/definitions/oauth2Scopes"
},
"tokenUrl": {
"type": "string",
"format": "uri"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"oauth2ApplicationSecurity": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"flow",
"tokenUrl"
],
"properties": {
"type": {
"type": "string",
"enum": [
"oauth2"
]
},
"flow": {
"type": "string",
"enum": [
"application"
]
},
"scopes": {
"$ref": "#/definitions/oauth2Scopes"
},
"tokenUrl": {
"type": "string",
"format": "uri"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"oauth2AccessCodeSecurity": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"flow",
"authorizationUrl",
"tokenUrl"
],
"properties": {
"type": {
"type": "string",
"enum": [
"oauth2"
]
},
"flow": {
"type": "string",
"enum": [
"accessCode"
]
},
"scopes": {
"$ref": "#/definitions/oauth2Scopes"
},
"authorizationUrl": {
"type": "string",
"format": "uri"
},
"tokenUrl": {
"type": "string",
"format": "uri"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
}
},
"oauth2Scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"mediaTypeList": {
"type": "array",
"items": {
"$ref": "#/definitions/mimeType"
},
"uniqueItems": true
},
"parametersList": {
"type": "array",
"description": "The parameters needed to send a valid API call.",
"additionalItems": false,
"items": {
"oneOf": [
{
"$ref": "#/definitions/parameter"
},
{
"$ref": "#/definitions/jsonReference"
}
]
},
"uniqueItems": true
},
"schemesList": {
"type": "array",
"description": "The transfer protocol of the API.",
"items": {
"type": "string",
"enum": [
"http",
"https",
"ws",
"wss"
]
},
"uniqueItems": true
},
"collectionFormat": {
"type": "string",
"enum": [
"csv",
"ssv",
"tsv",
"pipes"
],
"default": "csv"
},
"collectionFormatWithMulti": {
"type": "string",
"enum": [
"csv",
"ssv",
"tsv",
"pipes",
"multi"
],
"default": "csv"
},
"title": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/title"
},
"description": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/description"
},
"default": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/default"
},
"multipleOf": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf"
},
"maximum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/maximum"
},
"exclusiveMaximum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum"
},
"minimum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/minimum"
},
"exclusiveMinimum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum"
},
"maxLength": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
},
"minLength": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
},
"pattern": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/pattern"
},
"maxItems": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger"
},
"minItems": {
"$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0"
},
"uniqueItems": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems"
},
"enum": {
"$ref": "http://json-schema.org/draft-04/schema#/properties/enum"
},
"jsonReference": {
"type": "object",
"required": [
"$ref"
],
"additionalProperties": false,
"properties": {
"$ref": {
"type": "string"
}
}
}
}
} JSON-Validator-5.18/lib/JSON/Validator/cache/10a5eeb37fcd5d829449028f7ceb0774 0000644 0000765 0000024 00000076421 15210277065 024674 0 ustar jhthorsen staff type: object
required:
- openapi
- info
- paths
properties:
openapi:
type: string
pattern: ^3\.0\.\d(-.+)?$
info:
$ref: '#/definitions/Info'
externalDocs:
$ref: '#/definitions/ExternalDocumentation'
servers:
type: array
items:
$ref: '#/definitions/Server'
security:
type: array
items:
$ref: '#/definitions/SecurityRequirement'
tags:
type: array
items:
$ref: '#/definitions/Tag'
paths:
$ref: '#/definitions/Paths'
components:
$ref: '#/definitions/Components'
patternProperties:
'^x-': {}
additionalProperties: false
definitions:
Reference:
type: object
required:
- $ref
patternProperties:
'^\$ref$':
type: string
format: uri-reference
Info:
type: object
required:
- title
- version
properties:
title:
type: string
description:
type: string
termsOfService:
type: string
format: uri-reference
contact:
$ref: '#/definitions/Contact'
license:
$ref: '#/definitions/License'
version:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
Contact:
type: object
properties:
name:
type: string
url:
type: string
format: uri-reference
email:
type: string
format: email
patternProperties:
'^x-': {}
additionalProperties: false
License:
type: object
required:
- name
properties:
name:
type: string
url:
type: string
format: uri-reference
patternProperties:
'^x-': {}
additionalProperties: false
Server:
type: object
required:
- url
properties:
url:
type: string
description:
type: string
variables:
type: object
additionalProperties:
$ref: '#/definitions/ServerVariable'
patternProperties:
'^x-': {}
additionalProperties: false
ServerVariable:
type: object
required:
- default
properties:
enum:
type: array
items:
type: string
default:
type: string
description:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
Components:
type: object
properties:
schemas:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Schema'
responses:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Response'
parameters:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Parameter'
examples:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Example'
requestBodies:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/RequestBody'
headers:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Header'
securitySchemes:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/SecurityScheme'
links:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Link'
callbacks:
type: object
patternProperties:
'^[a-zA-Z0-9\.\-_]+$':
oneOf:
- $ref: '#/definitions/Reference'
- $ref: '#/definitions/Callback'
patternProperties:
'^x-': {}
additionalProperties: false
Schema:
type: object
properties:
title:
type: string
multipleOf:
type: number
minimum: 0
exclusiveMinimum: true
maximum:
type: number
exclusiveMaximum:
type: boolean
default: false
minimum:
type: number
exclusiveMinimum:
type: boolean
default: false
maxLength:
type: integer
minimum: 0
minLength:
type: integer
minimum: 0
default: 0
pattern:
type: string
format: regex
maxItems:
type: integer
minimum: 0
minItems:
type: integer
minimum: 0
default: 0
uniqueItems:
type: boolean
default: false
maxProperties:
type: integer
minimum: 0
minProperties:
type: integer
minimum: 0
default: 0
required:
type: array
items:
type: string
minItems: 1
uniqueItems: true
enum:
type: array
items: {}
minItems: 1
uniqueItems: true
type:
type: string
enum:
- array
- boolean
- integer
- number
- object
- string
not:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
allOf:
type: array
items:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
oneOf:
type: array
items:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
anyOf:
type: array
items:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
items:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
properties:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
additionalProperties:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
- type: boolean
default: true
description:
type: string
format:
type: string
default: {}
nullable:
type: boolean
default: false
discriminator:
$ref: '#/definitions/Discriminator'
readOnly:
type: boolean
default: false
writeOnly:
type: boolean
default: false
example: {}
externalDocs:
$ref: '#/definitions/ExternalDocumentation'
deprecated:
type: boolean
default: false
xml:
$ref: '#/definitions/XML'
patternProperties:
'^x-': {}
additionalProperties: false
Discriminator:
type: object
required:
- propertyName
properties:
propertyName:
type: string
mapping:
type: object
additionalProperties:
type: string
XML:
type: object
properties:
name:
type: string
namespace:
type: string
format: uri-reference
prefix:
type: string
attribute:
type: boolean
default: false
wrapped:
type: boolean
default: false
patternProperties:
'^x-': {}
additionalProperties: false
Response:
type: object
required:
- description
properties:
description:
type: string
headers:
additionalProperties:
oneOf:
- $ref: '#/definitions/Header'
- $ref: '#/definitions/Reference'
content:
type: object
additionalProperties:
$ref: '#/definitions/MediaType'
links:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Link'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
MediaType:
oneOf:
- $ref: '#/definitions/MediaTypeWithExample'
- $ref: '#/definitions/MediaTypeWithExamples'
MediaTypeWithExample:
type: object
properties:
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
example: {}
encoding:
type: object
additionalProperties:
$ref: '#/definitions/Encoding'
patternProperties:
'^x-': {}
additionalProperties: false
MediaTypeWithExamples:
type: object
required:
- examples
properties:
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Example'
- $ref: '#/definitions/Reference'
encoding:
type: object
additionalProperties:
$ref: '#/definitions/Encoding'
patternProperties:
'^x-': {}
additionalProperties: false
Example:
type: object
properties:
summary:
type: string
description:
type: string
value: {}
externalValue:
type: string
format: uri-reference
patternProperties:
'^x-': {}
additionalProperties: false
Header:
oneOf:
- $ref: '#/definitions/HeaderWithSchema'
- $ref: '#/definitions/HeaderWithContent'
HeaderWithSchema:
oneOf:
- $ref: '#/definitions/HeaderWithSchemaWithExample'
- $ref: '#/definitions/HeaderWithSchemaWithExamples'
HeaderWithSchemaWithExample:
type: object
required:
- schema
properties:
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- simple
default: simple
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
example: {}
patternProperties:
'^x-': {}
additionalProperties: false
HeaderWithSchemaWithExamples:
type: object
required:
- schema
- examples
properties:
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- simple
default: simple
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Example'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
HeaderWithContent:
type: object
required:
- content
properties:
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
content:
type: object
additionalProperties:
$ref: '#/definitions/MediaType'
minProperties: 1
maxProperties: 1
patternProperties:
'^x-': {}
additionalProperties: false
Paths:
type: object
patternProperties:
'^\/':
$ref: '#/definitions/PathItem'
'^x-': {}
additionalProperties: false
PathItem:
type: object
properties:
$ref:
type: string
summary:
type: string
description:
type: string
get:
$ref: '#/definitions/Operation'
put:
$ref: '#/definitions/Operation'
post:
$ref: '#/definitions/Operation'
delete:
$ref: '#/definitions/Operation'
options:
$ref: '#/definitions/Operation'
head:
$ref: '#/definitions/Operation'
patch:
$ref: '#/definitions/Operation'
trace:
$ref: '#/definitions/Operation'
servers:
type: array
items:
$ref: '#/definitions/Server'
parameters:
type: array
items:
oneOf:
- $ref: '#/definitions/Parameter'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
Operation:
type: object
required:
- responses
properties:
tags:
type: array
items:
type: string
summary:
type: string
description:
type: string
externalDocs:
$ref: '#/definitions/ExternalDocumentation'
operationId:
type: string
parameters:
type: array
items:
oneOf:
- $ref: '#/definitions/Parameter'
- $ref: '#/definitions/Reference'
requestBody:
oneOf:
- $ref: '#/definitions/RequestBody'
- $ref: '#/definitions/Reference'
responses:
$ref: '#/definitions/Responses'
callbacks:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Callback'
- $ref: '#/definitions/Reference'
deprecated:
type: boolean
default: false
security:
type: array
items:
$ref: '#/definitions/SecurityRequirement'
servers:
type: array
items:
$ref: '#/definitions/Server'
patternProperties:
'^x-': {}
additionalProperties: false
Responses:
type: object
properties:
default:
oneOf:
- $ref: '#/definitions/Response'
- $ref: '#/definitions/Reference'
patternProperties:
'[1-5](?:\d{2}|XX)':
oneOf:
- $ref: '#/definitions/Response'
- $ref: '#/definitions/Reference'
minProperties: 1
additionalProperties: false
SecurityRequirement:
type: object
additionalProperties:
type: array
items:
type: string
Tag:
type: object
required:
- name
properties:
name:
type: string
description:
type: string
externalDocs:
$ref: '#/definitions/ExternalDocumentation'
patternProperties:
'^x-': {}
additionalProperties: false
ExternalDocumentation:
type: object
required:
- url
properties:
description:
type: string
url:
type: string
format: uri-reference
patternProperties:
'^x-': {}
additionalProperties: false
Parameter:
oneOf:
- $ref: '#/definitions/ParameterWithSchema'
- $ref: '#/definitions/ParameterWithContent'
ParameterWithSchema:
oneOf:
- $ref: '#/definitions/ParameterWithSchemaWithExample'
- $ref: '#/definitions/ParameterWithSchemaWithExamples'
ParameterWithSchemaWithExample:
oneOf:
- $ref: '#/definitions/ParameterWithSchemaWithExampleInPath'
- $ref: '#/definitions/ParameterWithSchemaWithExampleInQuery'
- $ref: '#/definitions/ParameterWithSchemaWithExampleInHeader'
- $ref: '#/definitions/ParameterWithSchemaWithExampleInCookie'
ParameterWithSchemaWithExampleInPath:
type: object
required:
- name
- in
- schema
- required
properties:
name:
type: string
in:
type: string
enum:
- path
description:
type: string
required:
type: boolean
enum:
- true
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- matrix
- label
- simple
default: simple
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
example: {}
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExampleInQuery:
type: object
required:
- name
- in
- schema
properties:
name:
type: string
in:
type: string
enum:
- query
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- form
- spaceDelimited
- pipeDelimited
- deepObject
default: form
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
example: {}
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExampleInHeader:
type: object
required:
- name
- in
- schema
properties:
name:
type: string
in:
type: string
enum:
- header
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- simple
default: simple
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
example: {}
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExampleInCookie:
type: object
required:
- name
- in
- schema
properties:
name:
type: string
in:
type: string
enum:
- cookie
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- form
default: form
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
example: {}
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExamples:
oneOf:
- $ref: '#/definitions/ParameterWithSchemaWithExamplesInPath'
- $ref: '#/definitions/ParameterWithSchemaWithExamplesInQuery'
- $ref: '#/definitions/ParameterWithSchemaWithExamplesInHeader'
- $ref: '#/definitions/ParameterWithSchemaWithExamplesInCookie'
ParameterWithSchemaWithExamplesInPath:
type: object
required:
- name
- in
- schema
- required
- examples
properties:
name:
type: string
in:
type: string
enum:
- path
description:
type: string
required:
type: boolean
enum:
- true
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- matrix
- label
- simple
default: simple
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Example'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExamplesInQuery:
type: object
required:
- name
- in
- schema
- examples
properties:
name:
type: string
in:
type: string
enum:
- query
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- form
- spaceDelimited
- pipeDelimited
- deepObject
default: form
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Example'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExamplesInHeader:
type: object
required:
- name
- in
- schema
- examples
properties:
name:
type: string
in:
type: string
enum:
- header
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- simple
default: simple
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Example'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithSchemaWithExamplesInCookie:
type: object
required:
- name
- in
- schema
- examples
properties:
name:
type: string
in:
type: string
enum:
- cookie
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
style:
type: string
enum:
- form
default: form
explode:
type: boolean
allowReserved:
type: boolean
default: false
schema:
oneOf:
- $ref: '#/definitions/Schema'
- $ref: '#/definitions/Reference'
examples:
type: object
additionalProperties:
oneOf:
- $ref: '#/definitions/Example'
- $ref: '#/definitions/Reference'
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithContent:
oneOf:
- $ref: '#/definitions/ParameterWithContentInPath'
- $ref: '#/definitions/ParameterWithContentNotInPath'
ParameterWithContentInPath:
type: object
required:
- name
- in
- content
properties:
name:
type: string
in:
type: string
enum:
- path
description:
type: string
required:
type: boolean
enum:
- true
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
content:
type: object
additionalProperties:
$ref: '#/definitions/MediaType'
minProperties: 1
maxProperties: 1
patternProperties:
'^x-': {}
additionalProperties: false
ParameterWithContentNotInPath:
type: object
required:
- name
- in
- content
properties:
name:
type: string
in:
type: string
enum:
- query
- header
- cookie
description:
type: string
required:
type: boolean
default: false
deprecated:
type: boolean
default: false
allowEmptyValue:
type: boolean
default: false
content:
type: object
additionalProperties:
$ref: '#/definitions/MediaType'
minProperties: 1
maxProperties: 1
patternProperties:
'^x-': {}
additionalProperties: false
RequestBody:
type: object
required:
- content
properties:
description:
type: string
content:
type: object
additionalProperties:
$ref: '#/definitions/MediaType'
required:
type: boolean
default: false
patternProperties:
'^x-': {}
additionalProperties: false
SecurityScheme:
oneOf:
- $ref: '#/definitions/APIKeySecurityScheme'
- $ref: '#/definitions/HTTPSecurityScheme'
- $ref: '#/definitions/OAuth2SecurityScheme'
- $ref: '#/definitions/OpenIdConnectSecurityScheme'
APIKeySecurityScheme:
type: object
required:
- type
- name
- in
properties:
type:
type: string
enum:
- apiKey
name:
type: string
in:
type: string
enum:
- header
- query
- cookie
description:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
HTTPSecurityScheme:
oneOf:
- $ref: '#/definitions/NonBearerHTTPSecurityScheme'
- $ref: '#/definitions/BearerHTTPSecurityScheme'
NonBearerHTTPSecurityScheme:
type: object
required:
- scheme
- type
properties:
scheme:
type: string
not:
enum:
- bearer
description:
type: string
type:
type: string
enum:
- http
patternProperties:
'^x-': {}
additionalProperties: false
BearerHTTPSecurityScheme:
type: object
required:
- type
- scheme
properties:
scheme:
type: string
enum:
- bearer
bearerFormat:
type: string
type:
type: string
enum:
- http
description:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
OAuth2SecurityScheme:
type: object
required:
- type
- flows
properties:
type:
type: string
enum:
- oauth2
flows:
$ref: '#/definitions/OAuthFlows'
description:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
OpenIdConnectSecurityScheme:
type: object
required:
- type
- openIdConnectUrl
properties:
type:
type: string
enum:
- openIdConnect
openIdConnectUrl:
type: string
format: uri-reference
description:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
OAuthFlows:
type: object
properties:
implicit:
$ref: '#/definitions/ImplicitOAuthFlow'
password:
$ref: '#/definitions/PasswordOAuthFlow'
clientCredentials:
$ref: '#/definitions/ClientCredentialsFlow'
authorizationCode:
$ref: '#/definitions/AuthorizationCodeOAuthFlow'
patternProperties:
'^x-': {}
additionalProperties: false
ImplicitOAuthFlow:
type: object
required:
- authorizationUrl
- scopes
properties:
authorizationUrl:
type: string
format: uri-reference
refreshUrl:
type: string
format: uri-reference
scopes:
type: object
additionalProperties:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
PasswordOAuthFlow:
type: object
required:
- tokenUrl
properties:
tokenUrl:
type: string
format: uri-reference
refreshUrl:
type: string
format: uri-reference
scopes:
type: object
additionalProperties:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
ClientCredentialsFlow:
type: object
required:
- tokenUrl
properties:
tokenUrl:
type: string
format: uri-reference
refreshUrl:
type: string
format: uri-reference
scopes:
type: object
additionalProperties:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
AuthorizationCodeOAuthFlow:
type: object
required:
- authorizationUrl
- tokenUrl
properties:
authorizationUrl:
type: string
format: uri-reference
tokenUrl:
type: string
format: uri-reference
refreshUrl:
type: string
format: uri-reference
scopes:
type: object
additionalProperties:
type: string
patternProperties:
'^x-': {}
additionalProperties: false
Link:
oneOf:
- $ref: '#/definitions/LinkWithOperationRef'
- $ref: '#/definitions/LinkWithOperationId'
LinkWithOperationRef:
type: object
properties:
operationRef:
type: string
format: uri-reference
parameters:
type: object
additionalProperties: {}
requestBody: {}
description:
type: string
server:
$ref: '#/definitions/Server'
patternProperties:
'^x-': {}
additionalProperties: false
LinkWithOperationId:
type: object
properties:
operationId:
type: string
parameters:
type: object
additionalProperties: {}
requestBody: {}
description:
type: string
server:
$ref: '#/definitions/Server'
patternProperties:
'^x-': {}
additionalProperties: false
Callback:
type: object
additionalProperties:
$ref: '#/definitions/PathItem'
patternProperties:
'^x-': {}
Encoding:
type: object
properties:
contentType:
type: string
headers:
type: object
additionalProperties:
$ref: '#/definitions/Header'
style:
type: string
enum:
- form
- spaceDelimited
- pipeDelimited
- deepObject
explode:
type: boolean
allowReserved:
type: boolean
default: false
additionalProperties: false
JSON-Validator-5.18/lib/JSON/Validator/cache/d8cf7ae7a0fd14accadf5d18bc84d14f 0000644 0000765 0000024 00000005422 15210277065 025370 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/meta/validation",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/validation": true
},
"$recursiveAnchor": true,
"title": "Validation vocabulary meta-schema",
"type": ["object", "boolean"],
"properties": {
"multipleOf": {
"type": "number",
"exclusiveMinimum": 0
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "number"
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "number"
},
"maxLength": { "$ref": "#/$defs/nonNegativeInteger" },
"minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"maxItems": { "$ref": "#/$defs/nonNegativeInteger" },
"minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"maxContains": { "$ref": "#/$defs/nonNegativeInteger" },
"minContains": {
"$ref": "#/$defs/nonNegativeInteger",
"default": 1
},
"maxProperties": { "$ref": "#/$defs/nonNegativeInteger" },
"minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" },
"required": { "$ref": "#/$defs/stringArray" },
"dependentRequired": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/stringArray"
}
},
"const": true,
"enum": {
"type": "array",
"items": true
},
"type": {
"anyOf": [
{ "$ref": "#/$defs/simpleTypes" },
{
"type": "array",
"items": { "$ref": "#/$defs/simpleTypes" },
"minItems": 1,
"uniqueItems": true
}
]
}
},
"$defs": {
"nonNegativeInteger": {
"type": "integer",
"minimum": 0
},
"nonNegativeIntegerDefault0": {
"$ref": "#/$defs/nonNegativeInteger",
"default": 0
},
"simpleTypes": {
"enum": [
"array",
"boolean",
"integer",
"null",
"number",
"object",
"string"
]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true,
"default": []
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/a0f5b4b4e75ea17fc09e88ec0343d148 0000644 0000765 0000024 00000006523 15210277065 024737 0 ustar jhthorsen staff {
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"contact": { "name": "wordnik api team", "url": "http://developer.wordnik.com" },
"license": { "name": "Creative Commons 4.0 International", "url": "http://creativecommons.org/licenses/by/4.0/" }
},
"host": "petstore.swagger.wordnik.com",
"basePath": "/api",
"schemes": [ "http" ],
"parameters": {
"limit": {
"name": "limit",
"in": "query",
"description": "How many items to return at one time (max 100)",
"required": false,
"type": "integer",
"format": "int32"
}
},
"paths": {
"/pets": {
"get": {
"x-mojo-controller": "t::Api",
"tags": [ "pets" ],
"summary": "finds pets in the system",
"operationId": "listPets",
"parameters": [
{ "$ref": "#/parameters/limit" }
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"type": "array",
"items": { "$ref": "#/definitions/Pet" }
},
"headers": {
"x-expires": {
"type": "string"
}
}
},
"default": {
"description": "unexpected error",
"schema": { "$ref": "#/definitions/Error" }
}
}
},
"post": {
"x-mojo-controller": "t::Api",
"tags": [ "pets" ],
"summary": "add pets to the system",
"operationId": "addPet",
"parameters": [
{
"name": "data",
"in": "body",
"required": true,
"schema": {
"type": "object",
"parameters": {
"name": { "type": "string" },
"tag": { "type": "string" }
}
}
}
],
"responses": {
"200": {
"description": "pet response",
"schema": { "$ref": "#/definitions/Pet" }
},
"default": {
"description": "unexpected error",
"schema": { "$ref": "#/definitions/Error" }
}
}
}
},
"/pets/{petId}": {
"post": {
"x-mojo-controller": "t::Api",
"tags": [ "pets" ],
"summary": "Info for a specific pet",
"operationId": "showPetById",
"parameters": [
{
"name": "petId",
"in": "path",
"required": true,
"description": "The id of the pet to receive",
"type": "integer"
}
],
"responses": {
"200": {
"description": "Expected response to a valid request",
"schema": { "$ref": "#/definitions/Pet" }
},
"default": {
"description": "unexpected error",
"schema": { "$ref": "#/definitions/Error" }
}
}
}
}
},
"definitions": {
"Pet": {
"required": [ "id", "name" ],
"properties": {
"id": { "type": "integer", "format": "int64" },
"name": { "type": "string" },
"tag": { "type": "string" }
}
},
"Error": {
"required": [ "code", "message" ],
"properties": {
"code": { "type": "integer", "format": "int32" },
"message": { "type": "string" }
}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/630949337805585c8e52deea27d11419 0000644 0000765 0000024 00000000513 15210277065 024305 0 ustar jhthorsen staff {
"type" : "object",
"required": [ "errors" ],
"properties": {
"errors": {
"type": "array",
"items": {
"type" : "object",
"required": [ "message", "path" ],
"properties": {
"message": { "type": "string" },
"path": { "type": "string" }
}
}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/eaa832720f36cff0abc20c05236a9cd9 0000644 0000765 0000024 00000000513 15210277065 024767 0 ustar jhthorsen staff {
"type" : "object",
"required": [ "errors" ],
"properties": {
"errors": {
"type": "array",
"items": {
"type" : "object",
"required": [ "message", "path" ],
"properties": {
"message": { "type": "string" },
"path": { "type": "string" }
}
}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/089e74a6d17f64af17a9efd6d0fa0de6 0000644 0000765 0000024 00000001005 15210277065 025100 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/meta/content",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/content": true
},
"$recursiveAnchor": true,
"title": "Content vocabulary meta-schema",
"type": ["object", "boolean"],
"properties": {
"contentMediaType": { "type": "string" },
"contentEncoding": { "type": "string" },
"contentSchema": { "$recursiveRef": "#" }
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/c6f188eb288cf986f23db49297b25e83 0000644 0000765 0000024 00000003371 15210277065 024636 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/schema",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/core": true,
"https://json-schema.org/draft/2019-09/vocab/applicator": true,
"https://json-schema.org/draft/2019-09/vocab/validation": true,
"https://json-schema.org/draft/2019-09/vocab/meta-data": true,
"https://json-schema.org/draft/2019-09/vocab/format": false,
"https://json-schema.org/draft/2019-09/vocab/content": true
},
"$recursiveAnchor": true,
"title": "Core and Validation specifications meta-schema",
"allOf": [
{"$ref": "meta/core"},
{"$ref": "meta/applicator"},
{"$ref": "meta/validation"},
{"$ref": "meta/meta-data"},
{"$ref": "meta/format"},
{"$ref": "meta/content"}
],
"type": ["object", "boolean"],
"properties": {
"definitions": {
"$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.",
"type": "object",
"additionalProperties": { "$recursiveRef": "#" },
"default": {}
},
"dependencies": {
"$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"",
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$recursiveRef": "#" },
{ "$ref": "meta/validation#/$defs/stringArray" }
]
}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/7fe97ed1a4c3fac607dd276b2b298275 0000644 0000765 0000024 00000001574 15210277065 024753 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/meta/meta-data",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/meta-data": true
},
"$recursiveAnchor": true,
"title": "Meta-data vocabulary meta-schema",
"type": ["object", "boolean"],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": true,
"deprecated": {
"type": "boolean",
"default": false
},
"readOnly": {
"type": "boolean",
"default": false
},
"writeOnly": {
"type": "boolean",
"default": false
},
"examples": {
"type": "array",
"items": true
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/4a31fe43be9e23ca9eb8d9e9faba8892 0000644 0000765 0000024 00000011417 15210277065 025175 0 ustar jhthorsen staff {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://json-schema.org/draft-07/schema#",
"title": "Core schema meta-schema",
"definitions": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#" }
},
"nonNegativeInteger": {
"type": "integer",
"minimum": 0
},
"nonNegativeIntegerDefault0": {
"allOf": [
{ "$ref": "#/definitions/nonNegativeInteger" },
{ "default": 0 }
]
},
"simpleTypes": {
"enum": [
"array",
"boolean",
"integer",
"null",
"number",
"object",
"string"
]
},
"stringArray": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true,
"default": []
}
},
"type": ["object", "boolean"],
"properties": {
"$id": {
"type": "string",
"format": "uri-reference"
},
"$schema": {
"type": "string",
"format": "uri"
},
"$ref": {
"type": "string",
"format": "uri-reference"
},
"$comment": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"default": true,
"readOnly": {
"type": "boolean",
"default": false
},
"examples": {
"type": "array",
"items": true
},
"multipleOf": {
"type": "number",
"exclusiveMinimum": 0
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "number"
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "number"
},
"maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
"minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"pattern": {
"type": "string",
"format": "regex"
},
"additionalItems": { "$ref": "#" },
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": true
},
"maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
"minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"uniqueItems": {
"type": "boolean",
"default": false
},
"contains": { "$ref": "#" },
"maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
"minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
"required": { "$ref": "#/definitions/stringArray" },
"additionalProperties": { "$ref": "#" },
"definitions": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"propertyNames": { "format": "regex" },
"default": {}
},
"dependencies": {
"type": "object",
"additionalProperties": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/stringArray" }
]
}
},
"propertyNames": { "$ref": "#" },
"const": true,
"enum": {
"type": "array",
"items": true,
"minItems": 1,
"uniqueItems": true
},
"type": {
"anyOf": [
{ "$ref": "#/definitions/simpleTypes" },
{
"type": "array",
"items": { "$ref": "#/definitions/simpleTypes" },
"minItems": 1,
"uniqueItems": true
}
]
},
"format": { "type": "string" },
"contentMediaType": { "type": "string" },
"contentEncoding": { "type": "string" },
"if": {"$ref": "#"},
"then": {"$ref": "#"},
"else": {"$ref": "#"},
"allOf": { "$ref": "#/definitions/schemaArray" },
"anyOf": { "$ref": "#/definitions/schemaArray" },
"oneOf": { "$ref": "#/definitions/schemaArray" },
"not": { "$ref": "#" }
},
"default": true
}
JSON-Validator-5.18/lib/JSON/Validator/cache/4550dd8afbfee9e71377b45f5fea42ce 0000644 0000765 0000024 00000105650 15211411261 025162 0 ustar jhthorsen staff {
"id": "https://spec.openapis.org/oas/3.0/schema/2021-09-28",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "The description of OpenAPI v3.0.x documents, as defined by https://spec.openapis.org/oas/v3.0.3",
"type": "object",
"required": [
"openapi",
"info",
"paths"
],
"properties": {
"openapi": {
"type": "string",
"pattern": "^3\\.0\\.\\d(-.+)?$"
},
"info": {
"$ref": "#/definitions/Info"
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
},
"servers": {
"type": "array",
"items": {
"$ref": "#/definitions/Server"
}
},
"security": {
"type": "array",
"items": {
"$ref": "#/definitions/SecurityRequirement"
}
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/Tag"
},
"uniqueItems": true
},
"paths": {
"$ref": "#/definitions/Paths"
},
"components": {
"$ref": "#/definitions/Components"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"definitions": {
"Reference": {
"type": "object",
"required": [
"$ref"
],
"patternProperties": {
"^\\$ref$": {
"type": "string",
"format": "uri-reference"
}
}
},
"Info": {
"type": "object",
"required": [
"title",
"version"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"termsOfService": {
"type": "string",
"format": "uri-reference"
},
"contact": {
"$ref": "#/definitions/Contact"
},
"license": {
"$ref": "#/definitions/License"
},
"version": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Contact": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri-reference"
},
"email": {
"type": "string",
"format": "email"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"License": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri-reference"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Server": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string"
},
"description": {
"type": "string"
},
"variables": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ServerVariable"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ServerVariable": {
"type": "object",
"required": [
"default"
],
"properties": {
"enum": {
"type": "array",
"items": {
"type": "string"
}
},
"default": {
"type": "string"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Components": {
"type": "object",
"properties": {
"schemas": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"responses": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Response"
}
]
}
}
},
"parameters": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Parameter"
}
]
}
}
},
"examples": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Example"
}
]
}
}
},
"requestBodies": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/RequestBody"
}
]
}
}
},
"headers": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Header"
}
]
}
}
},
"securitySchemes": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/SecurityScheme"
}
]
}
}
},
"links": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Link"
}
]
}
}
},
"callbacks": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Callback"
}
]
}
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"multipleOf": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "boolean",
"default": false
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": false
},
"maxLength": {
"type": "integer",
"minimum": 0
},
"minLength": {
"type": "integer",
"minimum": 0,
"default": 0
},
"pattern": {
"type": "string",
"format": "regex"
},
"maxItems": {
"type": "integer",
"minimum": 0
},
"minItems": {
"type": "integer",
"minimum": 0,
"default": 0
},
"uniqueItems": {
"type": "boolean",
"default": false
},
"maxProperties": {
"type": "integer",
"minimum": 0
},
"minProperties": {
"type": "integer",
"minimum": 0,
"default": 0
},
"required": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"enum": {
"type": "array",
"items": {
},
"minItems": 1,
"uniqueItems": false
},
"type": {
"type": "string",
"enum": [
"array",
"boolean",
"integer",
"number",
"object",
"string"
]
},
"not": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"allOf": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"oneOf": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"anyOf": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"properties": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
},
{
"type": "boolean"
}
],
"default": true
},
"description": {
"type": "string"
},
"format": {
"type": "string"
},
"default": {
},
"nullable": {
"type": "boolean",
"default": false
},
"discriminator": {
"$ref": "#/definitions/Discriminator"
},
"readOnly": {
"type": "boolean",
"default": false
},
"writeOnly": {
"type": "boolean",
"default": false
},
"example": {
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
},
"deprecated": {
"type": "boolean",
"default": false
},
"xml": {
"$ref": "#/definitions/XML"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Discriminator": {
"type": "object",
"required": [
"propertyName"
],
"properties": {
"propertyName": {
"type": "string"
},
"mapping": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
},
"XML": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"namespace": {
"type": "string",
"format": "uri"
},
"prefix": {
"type": "string"
},
"attribute": {
"type": "boolean",
"default": false
},
"wrapped": {
"type": "boolean",
"default": false
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Response": {
"type": "object",
"required": [
"description"
],
"properties": {
"description": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Header"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
}
},
"links": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Link"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"MediaType": {
"type": "object",
"properties": {
"schema": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"example": {
},
"examples": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Example"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"encoding": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Encoding"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"allOf": [
{
"$ref": "#/definitions/ExampleXORExamples"
}
]
},
"Example": {
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"value": {
},
"externalValue": {
"type": "string",
"format": "uri-reference"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Header": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"default": false
},
"deprecated": {
"type": "boolean",
"default": false
},
"allowEmptyValue": {
"type": "boolean",
"default": false
},
"style": {
"type": "string",
"enum": [
"simple"
],
"default": "simple"
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"type": "boolean",
"default": false
},
"schema": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
},
"minProperties": 1,
"maxProperties": 1
},
"example": {
},
"examples": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Example"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"allOf": [
{
"$ref": "#/definitions/ExampleXORExamples"
},
{
"$ref": "#/definitions/SchemaXORContent"
}
]
},
"Paths": {
"type": "object",
"patternProperties": {
"^\\/": {
"$ref": "#/definitions/PathItem"
},
"^x-": {
}
},
"additionalProperties": false
},
"PathItem": {
"type": "object",
"properties": {
"$ref": {
"type": "string"
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"servers": {
"type": "array",
"items": {
"$ref": "#/definitions/Server"
}
},
"parameters": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Parameter"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"uniqueItems": true
}
},
"patternProperties": {
"^(get|put|post|delete|options|head|patch|trace)$": {
"$ref": "#/definitions/Operation"
},
"^x-": {
}
},
"additionalProperties": false
},
"Operation": {
"type": "object",
"required": [
"responses"
],
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
},
"operationId": {
"type": "string"
},
"parameters": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Parameter"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"uniqueItems": true
},
"requestBody": {
"oneOf": [
{
"$ref": "#/definitions/RequestBody"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"responses": {
"$ref": "#/definitions/Responses"
},
"callbacks": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Callback"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"deprecated": {
"type": "boolean",
"default": false
},
"security": {
"type": "array",
"items": {
"$ref": "#/definitions/SecurityRequirement"
}
},
"servers": {
"type": "array",
"items": {
"$ref": "#/definitions/Server"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Responses": {
"type": "object",
"properties": {
"default": {
"oneOf": [
{
"$ref": "#/definitions/Response"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"patternProperties": {
"^[1-5](?:\\d{2}|XX)$": {
"oneOf": [
{
"$ref": "#/definitions/Response"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"^x-": {
}
},
"minProperties": 1,
"additionalProperties": false
},
"SecurityRequirement": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"Tag": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ExternalDocumentation": {
"type": "object",
"required": [
"url"
],
"properties": {
"description": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri-reference"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ExampleXORExamples": {
"description": "Example and examples are mutually exclusive",
"not": {
"required": [
"example",
"examples"
]
}
},
"SchemaXORContent": {
"description": "Schema and content are mutually exclusive, at least one is required",
"not": {
"required": [
"schema",
"content"
]
},
"oneOf": [
{
"required": [
"schema"
]
},
{
"required": [
"content"
],
"description": "Some properties are not allowed if content is present",
"allOf": [
{
"not": {
"required": [
"style"
]
}
},
{
"not": {
"required": [
"explode"
]
}
},
{
"not": {
"required": [
"allowReserved"
]
}
},
{
"not": {
"required": [
"example"
]
}
},
{
"not": {
"required": [
"examples"
]
}
}
]
}
]
},
"Parameter": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"in": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"default": false
},
"deprecated": {
"type": "boolean",
"default": false
},
"allowEmptyValue": {
"type": "boolean",
"default": false
},
"style": {
"type": "string"
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"type": "boolean",
"default": false
},
"schema": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
},
"minProperties": 1,
"maxProperties": 1
},
"example": {
},
"examples": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Example"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"required": [
"name",
"in"
],
"allOf": [
{
"$ref": "#/definitions/ExampleXORExamples"
},
{
"$ref": "#/definitions/SchemaXORContent"
},
{
"$ref": "#/definitions/ParameterLocation"
}
]
},
"ParameterLocation": {
"description": "Parameter location",
"oneOf": [
{
"description": "Parameter in path",
"required": [
"required"
],
"properties": {
"in": {
"enum": [
"path"
]
},
"style": {
"enum": [
"matrix",
"label",
"simple"
],
"default": "simple"
},
"required": {
"enum": [
true
]
}
}
},
{
"description": "Parameter in query",
"properties": {
"in": {
"enum": [
"query"
]
},
"style": {
"enum": [
"form",
"spaceDelimited",
"pipeDelimited",
"deepObject"
],
"default": "form"
}
}
},
{
"description": "Parameter in header",
"properties": {
"in": {
"enum": [
"header"
]
},
"style": {
"enum": [
"simple"
],
"default": "simple"
}
}
},
{
"description": "Parameter in cookie",
"properties": {
"in": {
"enum": [
"cookie"
]
},
"style": {
"enum": [
"form"
],
"default": "form"
}
}
}
]
},
"RequestBody": {
"type": "object",
"required": [
"content"
],
"properties": {
"description": {
"type": "string"
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
}
},
"required": {
"type": "boolean",
"default": false
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"SecurityScheme": {
"oneOf": [
{
"$ref": "#/definitions/APIKeySecurityScheme"
},
{
"$ref": "#/definitions/HTTPSecurityScheme"
},
{
"$ref": "#/definitions/OAuth2SecurityScheme"
},
{
"$ref": "#/definitions/OpenIdConnectSecurityScheme"
}
]
},
"APIKeySecurityScheme": {
"type": "object",
"required": [
"type",
"name",
"in"
],
"properties": {
"type": {
"type": "string",
"enum": [
"apiKey"
]
},
"name": {
"type": "string"
},
"in": {
"type": "string",
"enum": [
"header",
"query",
"cookie"
]
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"HTTPSecurityScheme": {
"type": "object",
"required": [
"scheme",
"type"
],
"properties": {
"scheme": {
"type": "string"
},
"bearerFormat": {
"type": "string"
},
"description": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"http"
]
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"oneOf": [
{
"description": "Bearer",
"properties": {
"scheme": {
"type": "string",
"pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$"
}
}
},
{
"description": "Non Bearer",
"not": {
"required": [
"bearerFormat"
]
},
"properties": {
"scheme": {
"not": {
"type": "string",
"pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$"
}
}
}
}
]
},
"OAuth2SecurityScheme": {
"type": "object",
"required": [
"type",
"flows"
],
"properties": {
"type": {
"type": "string",
"enum": [
"oauth2"
]
},
"flows": {
"$ref": "#/definitions/OAuthFlows"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"OpenIdConnectSecurityScheme": {
"type": "object",
"required": [
"type",
"openIdConnectUrl"
],
"properties": {
"type": {
"type": "string",
"enum": [
"openIdConnect"
]
},
"openIdConnectUrl": {
"type": "string",
"format": "uri-reference"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"OAuthFlows": {
"type": "object",
"properties": {
"implicit": {
"$ref": "#/definitions/ImplicitOAuthFlow"
},
"password": {
"$ref": "#/definitions/PasswordOAuthFlow"
},
"clientCredentials": {
"$ref": "#/definitions/ClientCredentialsFlow"
},
"authorizationCode": {
"$ref": "#/definitions/AuthorizationCodeOAuthFlow"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ImplicitOAuthFlow": {
"type": "object",
"required": [
"authorizationUrl",
"scopes"
],
"properties": {
"authorizationUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"PasswordOAuthFlow": {
"type": "object",
"required": [
"tokenUrl",
"scopes"
],
"properties": {
"tokenUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ClientCredentialsFlow": {
"type": "object",
"required": [
"tokenUrl",
"scopes"
],
"properties": {
"tokenUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"AuthorizationCodeOAuthFlow": {
"type": "object",
"required": [
"authorizationUrl",
"tokenUrl",
"scopes"
],
"properties": {
"authorizationUrl": {
"type": "string",
"format": "uri-reference"
},
"tokenUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Link": {
"type": "object",
"properties": {
"operationId": {
"type": "string"
},
"operationRef": {
"type": "string",
"format": "uri-reference"
},
"parameters": {
"type": "object",
"additionalProperties": {
}
},
"requestBody": {
},
"description": {
"type": "string"
},
"server": {
"$ref": "#/definitions/Server"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"not": {
"description": "Operation Id and Operation Ref are mutually exclusive",
"required": [
"operationId",
"operationRef"
]
}
},
"Callback": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/PathItem"
},
"patternProperties": {
"^x-": {
}
}
},
"Encoding": {
"type": "object",
"properties": {
"contentType": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Header"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"style": {
"type": "string",
"enum": [
"form",
"spaceDelimited",
"pipeDelimited",
"deepObject"
]
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"type": "boolean",
"default": false
}
},
"additionalProperties": false
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/546acf85ddc442761c18517490215b90 0000644 0000765 0000024 00000003504 15210277065 024354 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/meta/applicator",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/applicator": true
},
"$recursiveAnchor": true,
"title": "Applicator vocabulary meta-schema",
"type": ["object", "boolean"],
"properties": {
"additionalItems": { "$recursiveRef": "#" },
"unevaluatedItems": { "$recursiveRef": "#" },
"items": {
"anyOf": [
{ "$recursiveRef": "#" },
{ "$ref": "#/$defs/schemaArray" }
]
},
"contains": { "$recursiveRef": "#" },
"additionalProperties": { "$recursiveRef": "#" },
"unevaluatedProperties": { "$recursiveRef": "#" },
"properties": {
"type": "object",
"additionalProperties": { "$recursiveRef": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$recursiveRef": "#" },
"propertyNames": { "format": "regex" },
"default": {}
},
"dependentSchemas": {
"type": "object",
"additionalProperties": {
"$recursiveRef": "#"
}
},
"propertyNames": { "$recursiveRef": "#" },
"if": { "$recursiveRef": "#" },
"then": { "$recursiveRef": "#" },
"else": { "$recursiveRef": "#" },
"allOf": { "$ref": "#/$defs/schemaArray" },
"anyOf": { "$ref": "#/$defs/schemaArray" },
"oneOf": { "$ref": "#/$defs/schemaArray" },
"not": { "$recursiveRef": "#" }
},
"$defs": {
"schemaArray": {
"type": "array",
"minItems": 1,
"items": { "$recursiveRef": "#" }
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/3be3f46eb248daf48925640f8ef057e8 0000644 0000765 0000024 00000002773 15210277065 024706 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/meta/core",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/core": true
},
"$recursiveAnchor": true,
"title": "Core vocabulary meta-schema",
"type": ["object", "boolean"],
"properties": {
"$id": {
"type": "string",
"format": "uri-reference",
"$comment": "Non-empty fragments not allowed.",
"pattern": "^[^#]*#?$"
},
"$schema": {
"type": "string",
"format": "uri"
},
"$anchor": {
"type": "string",
"pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$"
},
"$ref": {
"type": "string",
"format": "uri-reference"
},
"$recursiveRef": {
"type": "string",
"format": "uri-reference"
},
"$recursiveAnchor": {
"type": "boolean",
"default": false
},
"$vocabulary": {
"type": "object",
"propertyNames": {
"type": "string",
"format": "uri"
},
"additionalProperties": {
"type": "boolean"
}
},
"$comment": {
"type": "string"
},
"$defs": {
"type": "object",
"additionalProperties": { "$recursiveRef": "#" },
"default": {}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/d18065ce8fb1f748e766b2737bae5200 0000644 0000765 0000024 00000000623 15210277065 024577 0 ustar jhthorsen staff {
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://json-schema.org/draft/2019-09/meta/format",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/format": true
},
"$recursiveAnchor": true,
"title": "Format vocabulary meta-schema",
"type": ["object", "boolean"],
"properties": {
"format": { "type": "string" }
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/ea34d47d4e060a1c3b12d2287aff89a7 0000644 0000765 0000024 00000002471 15210277065 024725 0 ustar jhthorsen staff {
"title": "JSON schema for JSONPatch files",
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/operation"
},
"definitions": {
"operation": {
"type": "object",
"required": [ "op", "path" ],
"allOf": [ { "$ref": "#/definitions/path" } ],
"oneOf": [
{
"required": [ "value" ],
"properties": {
"op": {
"description": "The operation to perform.",
"type": "string",
"enum": [ "add", "replace", "test" ]
},
"value": {
"description": "The value to add, replace or test."
}
}
},
{
"properties": {
"op": {
"description": "The operation to perform.",
"type": "string",
"enum": [ "remove" ]
}
}
},
{
"required": [ "from" ],
"properties": {
"op": {
"description": "The operation to perform.",
"type": "string",
"enum": [ "move", "copy" ]
},
"from": {
"description": "A JSON Pointer path pointing to the location to move/copy from.",
"type": "string"
}
}
}
]
},
"path": {
"properties": {
"path": {
"description": "A JSON Pointer path.",
"type": "string"
}
}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/33912dbbde6e1d936140f1c82b283d01 0000644 0000765 0000024 00000073252 15210277065 024563 0 ustar jhthorsen staff {
"$id": "https://spec.openapis.org/oas/3.1/schema/2021-05-20",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"openapi": {
"type": "string",
"pattern": "^3\\.1\\.\\d+(-.+)?$"
},
"info": {
"$ref": "#/$defs/info"
},
"jsonSchemaDialect": {
"type": "string",
"format": "uri",
"default": "https://spec.openapis.org/oas/3.1/dialect/base"
},
"servers": {
"type": "array",
"items": {
"$ref": "#/$defs/server"
}
},
"paths": {
"$ref": "#/$defs/paths"
},
"webhooks": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/path-item-or-reference"
}
},
"components": {
"$ref": "#/$defs/components"
},
"security": {
"type": "array",
"items": {
"$ref": "#/$defs/security-requirement"
}
},
"tags": {
"type": "array",
"items": {
"$ref": "#/$defs/tag"
}
},
"externalDocs": {
"$ref": "#/$defs/external-documentation"
}
},
"required": [
"openapi",
"info"
],
"anyOf": [
{
"required": [
"paths"
]
},
{
"required": [
"components"
]
},
{
"required": [
"webhooks"
]
}
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false,
"$defs": {
"info": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"termsOfService": {
"type": "string"
},
"contact": {
"$ref": "#/$defs/contact"
},
"license": {
"$ref": "#/$defs/license"
},
"version": {
"type": "string"
}
},
"required": [
"title",
"version"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"contact": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string"
},
"email": {
"type": "string"
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"license": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"identifier": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
}
},
"required": [
"name"
],
"oneOf": [
{
"required": [
"identifier"
]
},
{
"required": [
"url"
]
}
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"server": {
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri-reference"
},
"description": {
"type": "string"
},
"variables": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/server-variable"
}
}
},
"required": [
"url"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"server-variable": {
"type": "object",
"properties": {
"enum": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"default": {
"type": "string"
},
"descriptions": {
"type": "string"
}
},
"required": [
"default"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"components": {
"type": "object",
"properties": {
"schemas": {
"type": "object",
"additionalProperties": {
"$dynamicRef": "#meta"
}
},
"responses": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/response-or-reference"
}
},
"parameters": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/parameter-or-reference"
}
},
"examples": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/example-or-reference"
}
},
"requestBodies": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/request-body-or-reference"
}
},
"headers": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/header-or-reference"
}
},
"securitySchemes": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/security-scheme-or-reference"
}
},
"links": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/link-or-reference"
}
},
"callbacks": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/callbacks-or-reference"
}
},
"pathItems": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/path-item-or-reference"
}
}
},
"patternProperties": {
"^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": {
"$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected",
"propertyNames": {
"pattern": "^[a-zA-Z0-9._-]+$"
}
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"paths": {
"type": "object",
"patternProperties": {
"^/": {
"$ref": "#/$defs/path-item"
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"path-item": {
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"servers": {
"type": "array",
"items": {
"$ref": "#/$defs/server"
}
},
"parameters": {
"type": "array",
"items": {
"$ref": "#/$defs/parameter-or-reference"
}
}
},
"patternProperties": {
"^(get|put|post|delete|options|head|patch|trace)$": {
"$ref": "#/$defs/operation"
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"path-item-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/path-item"
}
},
"operation": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/$defs/external-documentation"
},
"operationId": {
"type": "string"
},
"parameters": {
"type": "array",
"items": {
"$ref": "#/$defs/parameter-or-reference"
}
},
"requestBody": {
"$ref": "#/$defs/request-body-or-reference"
},
"responses": {
"$ref": "#/$defs/responses"
},
"callbacks": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/callbacks-or-reference"
}
},
"deprecated": {
"default": false,
"type": "boolean"
},
"security": {
"type": "array",
"items": {
"$ref": "#/$defs/security-requirement"
}
},
"servers": {
"type": "array",
"items": {
"$ref": "#/$defs/server"
}
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"external-documentation": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
}
},
"required": [
"url"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"parameter": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"in": {
"enum": [
"query",
"header",
"path",
"cookie"
]
},
"description": {
"type": "string"
},
"required": {
"default": false,
"type": "boolean"
},
"deprecated": {
"default": false,
"type": "boolean"
},
"allowEmptyValue": {
"default": false,
"type": "boolean"
},
"schema": {
"$dynamicRef": "#meta"
},
"content": {
"$ref": "#/$defs/content"
}
},
"required": [
"in"
],
"oneOf": [
{
"required": [
"schema"
]
},
{
"required": [
"content"
]
}
],
"dependentSchemas": {
"schema": {
"properties": {
"style": {
"type": "string"
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"default": false,
"type": "boolean"
}
},
"allOf": [
{
"$ref": "#/$defs/examples"
},
{
"$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path"
},
{
"$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header"
},
{
"$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query"
},
{
"$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie"
},
{
"$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-form"
}
],
"$defs": {
"styles-for-path": {
"if": {
"properties": {
"in": {
"const": "path"
}
},
"required": [
"in"
]
},
"then": {
"properties": {
"style": {
"default": "simple",
"enum": [
"matrix",
"label",
"simple"
]
},
"required": {
"const": true
}
},
"required": [
"required"
]
}
},
"styles-for-header": {
"if": {
"properties": {
"in": {
"const": "header"
}
},
"required": [
"in"
]
},
"then": {
"properties": {
"style": {
"default": "simple",
"enum": [
"simple"
]
}
}
}
},
"styles-for-query": {
"if": {
"properties": {
"in": {
"const": "query"
}
},
"required": [
"in"
]
},
"then": {
"properties": {
"style": {
"default": "form",
"enum": [
"form",
"spaceDelimited",
"pipeDelimited",
"deepObject"
]
}
}
}
},
"styles-for-cookie": {
"if": {
"properties": {
"in": {
"const": "cookie"
}
},
"required": [
"in"
]
},
"then": {
"properties": {
"style": {
"default": "form",
"enum": [
"form"
]
}
}
}
},
"styles-for-form": {
"if": {
"properties": {
"style": {
"const": "form"
}
},
"required": [
"style"
]
},
"then": {
"properties": {
"explode": {
"default": true
}
}
},
"else": {
"properties": {
"explode": {
"default": false
}
}
}
}
}
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"parameter-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/parameter"
}
},
"request-body": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"content": {
"$ref": "#/$defs/content"
},
"required": {
"default": false,
"type": "boolean"
}
},
"required": [
"content"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"request-body-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/request-body"
}
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/media-type"
},
"propertyNames": {
"format": "media-range"
}
},
"media-type": {
"type": "object",
"properties": {
"schema": {
"$dynamicRef": "#meta"
},
"encoding": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/encoding"
}
}
},
"allOf": [
{
"$ref": "#/$defs/specification-extensions"
},
{
"$ref": "#/$defs/examples"
}
],
"unevaluatedProperties": false
},
"encoding": {
"type": "object",
"properties": {
"contentType": {
"type": "string",
"format": "media-range"
},
"headers": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/header-or-reference"
}
},
"style": {
"default": "form",
"enum": [
"form",
"spaceDelimited",
"pipeDelimited",
"deepObject"
]
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"default": false,
"type": "boolean"
}
},
"allOf": [
{
"$ref": "#/$defs/specification-extensions"
},
{
"$ref": "#/$defs/encoding/$defs/explode-default"
}
],
"unevaluatedProperties": false,
"$defs": {
"explode-default": {
"if": {
"properties": {
"style": {
"const": "form"
}
},
"required": [
"style"
]
},
"then": {
"properties": {
"explode": {
"default": true
}
}
},
"else": {
"properties": {
"explode": {
"default": false
}
}
}
}
}
},
"responses": {
"type": "object",
"properties": {
"default": {
"$ref": "#/$defs/response-or-reference"
}
},
"patternProperties": {
"^[1-5][0-9X]{2}$": {
"$ref": "#/$defs/response-or-reference"
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"response": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/header-or-reference"
}
},
"content": {
"$ref": "#/$defs/content"
},
"links": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/link-or-reference"
}
}
},
"required": [
"description"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"response-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/response"
}
},
"callbacks": {
"type": "object",
"$ref": "#/$defs/specification-extensions",
"additionalProperties": {
"$ref": "#/$defs/path-item-or-reference"
}
},
"callbacks-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/callbacks"
}
},
"example": {
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"value": true,
"externalValue": {
"type": "string",
"format": "uri"
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"example-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/example"
}
},
"link": {
"type": "object",
"properties": {
"operationRef": {
"type": "string",
"format": "uri-reference"
},
"operationId": true,
"parameters": {
"$ref": "#/$defs/map-of-strings"
},
"requestBody": true,
"description": {
"type": "string"
},
"body": {
"$ref": "#/$defs/server"
}
},
"oneOf": [
{
"required": [
"operationRef"
]
},
{
"required": [
"operationId"
]
}
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"link-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/link"
}
},
"header": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"required": {
"default": false,
"type": "boolean"
},
"deprecated": {
"default": false,
"type": "boolean"
},
"allowEmptyValue": {
"default": false,
"type": "boolean"
}
},
"dependentSchemas": {
"schema": {
"properties": {
"style": {
"default": "simple",
"enum": [
"simple"
]
},
"explode": {
"default": false,
"type": "boolean"
},
"allowReserved": {
"default": false,
"type": "boolean"
},
"schema": {
"$dynamicRef": "#meta"
}
},
"$ref": "#/$defs/examples"
},
"content": {
"properties": {
"content": {
"$ref": "#/$defs/content"
}
}
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"header-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/header"
}
},
"tag": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/$defs/external-documentation"
}
},
"required": [
"name"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"reference": {
"type": "object",
"properties": {
"$ref": {
"type": "string",
"format": "uri-reference"
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
}
},
"unevaluatedProperties": false
},
"schema": {
"$dynamicAnchor": "meta",
"type": [
"object",
"boolean"
]
},
"security-scheme": {
"type": "object",
"properties": {
"type": {
"enum": [
"apiKey",
"http",
"mutualTLS",
"oauth2",
"openIdConnect"
]
},
"description": {
"type": "string"
}
},
"required": [
"type"
],
"allOf": [
{
"$ref": "#/$defs/specification-extensions"
},
{
"$ref": "#/$defs/security-scheme/$defs/type-apikey"
},
{
"$ref": "#/$defs/security-scheme/$defs/type-http"
},
{
"$ref": "#/$defs/security-scheme/$defs/type-http-bearer"
},
{
"$ref": "#/$defs/security-scheme/$defs/type-oauth2"
},
{
"$ref": "#/$defs/security-scheme/$defs/type-oidc"
}
],
"unevaluatedProperties": false,
"$defs": {
"type-apikey": {
"if": {
"properties": {
"type": {
"const": "apiKey"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"name": {
"type": "string"
},
"in": {
"enum": [
"query",
"header",
"cookie"
]
}
},
"required": [
"name",
"in"
]
}
},
"type-http": {
"if": {
"properties": {
"type": {
"const": "http"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"scheme": {
"type": "string"
}
},
"required": [
"scheme"
]
}
},
"type-http-bearer": {
"if": {
"properties": {
"type": {
"const": "http"
},
"scheme": {
"const": "bearer"
}
},
"required": [
"type",
"scheme"
]
},
"then": {
"properties": {
"bearerFormat": {
"type": "string"
}
},
"required": [
"scheme"
]
}
},
"type-oauth2": {
"if": {
"properties": {
"type": {
"const": "oauth2"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"flows": {
"$ref": "#/$defs/oauth-flows"
}
},
"required": [
"flows"
]
}
},
"type-oidc": {
"if": {
"properties": {
"type": {
"const": "openIdConnect"
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"openIdConnectUrl": {
"type": "string",
"format": "uri"
}
},
"required": [
"openIdConnectUrl"
]
}
}
}
},
"security-scheme-or-reference": {
"if": {
"required": [
"$ref"
]
},
"then": {
"$ref": "#/$defs/reference"
},
"else": {
"$ref": "#/$defs/security-scheme"
}
},
"oauth-flows": {
"type": "object",
"properties": {
"implicit": {
"$ref": "#/$defs/oauth-flows/$defs/implicit"
},
"password": {
"$ref": "#/$defs/oauth-flows/$defs/password"
},
"clientCredentials": {
"$ref": "#/$defs/oauth-flows/$defs/client-credentials"
},
"authorizationCode": {
"$ref": "#/$defs/oauth-flows/$defs/authorization-code"
}
},
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false,
"$defs": {
"implicit": {
"type": "object",
"properties": {
"authorizationUrl": {
"type": "string"
},
"refreshUrl": {
"type": "string"
},
"scopes": {
"$ref": "#/$defs/map-of-strings"
}
},
"required": [
"authorizationUrl",
"scopes"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"password": {
"type": "object",
"properties": {
"tokenUrl": {
"type": "string"
},
"refreshUrl": {
"type": "string"
},
"scopes": {
"$ref": "#/$defs/map-of-strings"
}
},
"required": [
"tokenUrl",
"scopes"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"client-credentials": {
"type": "object",
"properties": {
"tokenUrl": {
"type": "string"
},
"refreshUrl": {
"type": "string"
},
"scopes": {
"$ref": "#/$defs/map-of-strings"
}
},
"required": [
"tokenUrl",
"scopes"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
},
"authorization-code": {
"type": "object",
"properties": {
"authorizationUrl": {
"type": "string"
},
"tokenUrl": {
"type": "string"
},
"refreshUrl": {
"type": "string"
},
"scopes": {
"$ref": "#/$defs/map-of-strings"
}
},
"required": [
"authorizationUrl",
"tokenUrl",
"scopes"
],
"$ref": "#/$defs/specification-extensions",
"unevaluatedProperties": false
}
}
},
"security-requirement": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"specification-extensions": {
"patternProperties": {
"^x-": true
}
},
"examples": {
"properties": {
"example": true,
"examples": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/example-or-reference"
}
}
}
},
"map-of-strings": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
JSON-Validator-5.18/lib/JSON/Validator/cache/49c95b866e40f788892a7fb3c816b0e8 0000644 0000765 0000024 00000010427 15210277065 024555 0 ustar jhthorsen staff {
"id": "http://json-schema.org/draft-04/schema#",
"$schema": "http://json-schema.org/draft-04/schema#",
"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
}
]
},
"allOf": { "$ref": "#/definitions/schemaArray" },
"anyOf": { "$ref": "#/definitions/schemaArray" },
"oneOf": { "$ref": "#/definitions/schemaArray" },
"not": { "$ref": "#" }
},
"dependencies": {
"exclusiveMaximum": [ "maximum" ],
"exclusiveMinimum": [ "minimum" ]
},
"default": {}
}
JSON-Validator-5.18/lib/JSON/Validator/Error.pm 0000644 0000765 0000024 00000013030 15211411456 020722 0 ustar jhthorsen staff package JSON::Validator::Error;
use Mojo::Base -base;
use overload q("") => \&to_string, bool => sub {1}, fallback => 1;
our $MESSAGES = {
allOf => {type => '/allOf Expected %3 - got %4.'},
anyOf => {type => '/anyOf Expected %3 - got %4.'},
array => {
additionalItems => 'Invalid number of items: %3/%4.',
maxContains => 'Contains too many items: %3/%4.',
maxItems => 'Too many items: %3/%4.',
minContains => 'Contains not enough items: %3/%4.',
minItems => 'Not enough items: %3/%4.',
uniqueItems => 'Unique items required.',
contains => 'No items contained.',
},
const => {const => 'Does not match const: %3.'},
enum => {enum => 'Not in enum list: %3.'},
integer => {
ex_maximum => '%3 >= maximum(%4)',
ex_minimum => '%3 <= minimum(%4)',
maximum => '%3 > maximum(%4)',
minimum => '%3 < minimum(%4)',
multipleOf => 'Not multiple of %3.',
},
not => {not => 'Should not match.'},
null => {type => 'Not null.'},
number => {
ex_maximum => '%3 >= maximum(%4)',
ex_minimum => '%3 <= minimum(%4)',
maximum => '%3 > maximum(%4)',
minimum => '%3 < minimum(%4)',
multipleOf => 'Not multiple of %3.',
},
object => {
additionalProperties => 'Properties not allowed: %3.',
maxProperties => 'Too many properties: %3/%4.',
minProperties => 'Not enough properties: %3/%4.',
required => 'Missing property.',
dependencies => 'Missing property. Dependee: %3.',
},
oneOf => {
all_rules_match => 'All of the oneOf rules match.',
n_rules_match => 'oneOf rules %3 match.',
type => '/oneOf Expected %3 - got %4.',
},
string => {
pattern => 'String does not match %3.',
maxLength => 'String is too long: %3/%4.',
minLength => 'String is too short: %3/%4.',
}
};
has details => sub { [qw(generic generic)] };
has message => sub {
my $self = shift;
my $details = $self->details;
my $message;
if (($details->[0] || '') eq 'format') {
$message = '%3';
}
elsif (($details->[1] || '') eq 'type' and @$details == 3) {
$message = 'Expected %1 - got %3.';
}
elsif (my $group = $MESSAGES->{$details->[0]}) {
$message = $group->{$details->[1] || 'default'};
}
return join ' ', Failed => @$details unless defined $message;
$message =~ s!\%(\d)\b!{$details->[$1 - 1] // ''}!ge;
return $message;
};
has path => '/';
sub new {
my $class = shift;
return $class->SUPER::new unless @_;
# Constructed with attributes
return $class->SUPER::new($_[0]) if ref $_[0] eq 'HASH';
# Constructed with ($path, ...)
my $self = $class->SUPER::new;
my $path = ref $_[0] ? join '/', '', map { s!~!~0!g; s!/!~1!g; $_ } @{shift(@_)} : shift || '/';
$self->{path} = $path || '/';
# Constructed with ($path, $message) or ($path, \@details)
return !@_ ? $self : ref $_[0] ? $self->details(shift) : $self->message(shift);
}
sub to_string { sprintf '%s: %s', $_[0]->path, $_[0]->message }
sub TO_JSON { {message => $_[0]->message, path => $_[0]->path} }
1;
=encoding utf8
=head1 NAME
JSON::Validator::Error - JSON::Validator error object
=head1 SYNOPSIS
use JSON::Validator::Error;
my $err = JSON::Validator::Error->new($path, $message);
=head1 DESCRIPTION
L is a class representing validation errors from
L.
=head1 ATTRIBUTES
=head2 details
my $error = $error->details(["generic", "generic"]);
my $error = $error->details([qw(array type object)]);
my $error = $error->details([qw(format date-time Invalid)]);
my $array_ref = $error->details;
Details about the error:
=over 2
=item 1.
Often the category of tests that was run. Example values: allOf, anyOf, array,
const, enum, format, integer, not, null, number, object, oneOf and string.
=item 2.
Often the test that failed. Example values: additionalItems,
additionalProperties, const, enum, maxItems, maxLength, maxProperties, maximum,
minItems, minLength. minProperties, minimum, multipleOf, not, null, pattern,
required, type and uniqueItems,
=item 3.
The rest of the list contains parameters for the test that failed. It can be a
plain human-readable string or numbers indicating things such as max/min
values.
=back
=head2 message
my $str = $error->message;
A human readable description of the error. Defaults to being being constructed
from L. See the C<$MESSAGES> variable in the source code for more
details.
As an EXPERIMENTAL hack you can localize C<$JSON::Validator::Error::MESSAGES>
to get i18n support. Example:
sub validate_i18n {
local $JSON::Validator::Error::MESSAGES = {
allOf => {type => '/allOf Forventet %3 - fikk %4.'},
};
my @error_norwegian = $jv->validate({age => 42});
}
Note that the error messages might contain a mix of English and the local
language. Run some tests to see how it looks.
=head2 path
my $str = $error->path;
A JSON pointer to where the error occurred. Defaults to "/".
=head1 METHODS
=head2 new
my $error = JSON::Validator::Error->new(\%attributes);
my $error = JSON::Validator::Error->new($path, \@details);
my $error = JSON::Validator::Error->new($path, \@details);
Object constructor.
=head2 to_string
my $str = $error->to_string;
Returns the "path" and "message" part as a string: "$path: $message".
=head1 OPERATORS
L overloads the following operators:
=head2 bool
my $bool = !!$error;
Always true.
=head2 stringify
my $str = "$error";
Alias for L.
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Util.pm 0000644 0000765 0000024 00000021720 15211411456 020553 0 ustar jhthorsen staff package JSON::Validator::Util;
use Mojo::Base -strict;
use B;
use Carp ();
use Exporter 'import';
use JSON::Validator::Error;
use List::Util;
use Mojo::Collection;
use Mojo::JSON;
use Mojo::Loader;
use Mojo::Util;
use Scalar::Util 'blessed';
use constant SEREAL_SUPPORT => !$ENV{JSON_VALIDATOR_NO_SEREAL} && eval 'use Sereal::Encoder 4.00;1';
use constant CORE_BOOL => defined &builtin::is_bool;
our @EXPORT_OK = (
qw(E data_checksum data_section data_type is_bool is_num is_type),
qw(negotiate_content_type prefix_errors schema_type str2data),
);
sub E { JSON::Validator::Error->new(@_) }
my $serializer = SEREAL_SUPPORT ? \&_sereal_encode : \&_yaml_dump;
sub data_checksum {
return Mojo::Util::md5_sum(ref $_[0] ? $serializer->($_[0]) : defined $_[0] ? do { utf8::encode(my $x = shift); qq('$x') } : 'undef');
}
sub data_section {
my ($class, $file, $params) = @_;
state $skip_re = qr{(^JSON::Validator|^Mojo::Base$|^Mojolicious$|\w+::_Dynamic)};
my @classes = $class ? ([$class]) : ();
unless (@classes) {
my $i = 0;
while ($class = caller($i++)) {
push @classes, [$class] unless $class =~ $skip_re;
}
}
for my $group (@classes) {
push @$group, grep { !/$skip_re/ } do { no strict 'refs'; @{"$group->[0]\::ISA"} };
for my $class (@$group) {
next unless my $text = Mojo::Loader::data_section($class, $file);
return Mojo::Util::encode($params->{encoding}, $text) if $params->{encoding};
return $text;
}
}
return undef unless $params->{confess};
my $err = Mojo::JSON::encode_json([map { @$_ == 1 ? $_->[0] : $_ } @classes]);
Carp::confess(qq(Could not find "$file" in __DATA__ section of $err.));
}
sub data_type {
my $ref = ref $_[0];
my $blessed = blessed $_[0];
return 'object' if $ref eq 'HASH';
return 'file' if $ref eq 'Mojo::Upload';
return lc $ref if $ref and !$blessed;
return 'null' if !defined $_[0];
return 'boolean' if $blessed and ("$_[0]" eq "1" or !"$_[0]");
if (is_num($_[0])) {
return 'integer' if grep { ($_->{type} // '') eq 'integer' } @{$_[1] || []};
return 'number';
}
return $blessed || 'string';
}
sub is_bool {
if (CORE_BOOL) {
BEGIN { warnings->unimport('experimental::builtin') if CORE_BOOL }
return !!1 if builtin::is_bool $_[0];
}
blessed $_[0] && ($_[0]->isa('JSON::PP::Boolean') || "$_[0]" eq "1" || !$_[0]);
}
sub is_num { B::svref_2object(\$_[0])->FLAGS & (B::SVp_IOK | B::SVp_NOK) && 0 + $_[0] eq $_[0] && $_[0] * 0 == 0 }
sub is_type { blessed $_[0] ? $_[0]->isa($_[1]) : ref $_[0] eq $_[1] }
sub negotiate_content_type {
my ($accepts, $header) = @_;
return '' unless $header;
my %header_map = map {
/^\s*([^,; ]+)(?:\s*\;\s*q\s*=\s*(\d+(?:\.\d+)?))?\s*$/i ? (lc $1, $2 // -3)
: /^\s*([^,; ]+)(?:\s*\;\s*\w+\s*=\S+)?\s*$/i ? (lc $1, -1)
: (lc $_, -2);
} split /,/, $header;
my @headers = sort { $header_map{$b} <=> $header_map{$a} } sort keys %header_map;
# Check for exact match
for my $ct (@$accepts) {
return $ct if exists $header_map{$ct};
}
# Check for closest match
for my $re (map { my $re = "$_"; $re =~ s!\*!.*!g; $re = qr{$re}; [$_, $re] } grep {/\*/} @$accepts) {
for my $ct (@headers) {
return $re->[0] if $ct =~ $re->[1];
}
}
for my $re (map { local $_ = "$_"; s!\*!.*!g; qr{$_} } grep {/\*/} @headers) {
for my $ct (@$accepts) {
return $ct if $ct =~ $re;
}
}
# Could not find any valid content type
return '';
}
sub prefix_errors {
my ($type, @errors_with_index) = @_;
my @errors;
for my $e (@errors_with_index) {
my $index = shift @$e;
push @errors, map {
my $msg = sprintf '/%s/%s %s', $type, $index, $_->message;
$msg =~ s!(\d+)\s/!$1/!g;
E +{%$_, message => $msg}; # preserve 'details', for later introspection
} @$e;
}
return @errors;
}
sub schema_type {
return '' if ref $_[0] ne 'HASH';
return $_[0]->{type} if $_[0]->{type};
return _guessed_right(object => $_[1]) if $_[0]->{additionalProperties};
return _guessed_right(object => $_[1]) if $_[0]->{patternProperties};
return _guessed_right(object => $_[1]) if $_[0]->{properties};
return _guessed_right(object => $_[1]) if exists $_[0]->{propertyNames};
return _guessed_right(object => $_[1]) if $_[0]->{required};
return _guessed_right(object => $_[1])
if $_[0]->{dependencies}
or $_[0]->{dependentSchemas}
or $_[0]->{dependentRequired};
return _guessed_right(object => $_[1]) if defined $_[0]->{maxProperties} or defined $_[0]->{minProperties};
# additionalItems is intentionally omitted - it requires 'items' to take effect
return _guessed_right(array => $_[1]) if exists $_[0]->{items};
return _guessed_right(array => $_[1]) if $_[0]->{uniqueItems};
return _guessed_right(array => $_[1]) if exists $_[0]->{contains};
return _guessed_right(array => $_[1]) if exists $_[0]->{maxItems} or exists $_[0]->{minItems};
return _guessed_right(string => $_[1]) if $_[0]->{pattern};
return _guessed_right(string => $_[1]) if exists $_[0]->{maxLength} or defined $_[0]->{minLength};
return _guessed_right(number => $_[1]) if $_[0]->{multipleOf};
return _guessed_right(number => $_[1])
if defined $_[0]->{maximum}
or defined $_[0]->{minimum}
or defined $_[0]->{exclusiveMaximum}
or defined $_[0]->{exclusiveMinimum};
return 'const' if exists $_[0]->{const};
return '';
}
sub str2data {
my $data;
eval { $data = $_[0] =~ m!^\s*\{!s ? Mojo::JSON::decode_json($_[0]) : _yaml_load($_[0]); 1 } // Carp::confess($@);
return $data;
}
# _guessed_right($type, $data);
sub _guessed_right {
return $_[0] if !defined $_[1];
return $_[0] if $_[0] eq data_type $_[1], [{type => $_[0]}];
return '';
}
sub _sereal_encode {
state $s = Sereal::Encoder->new({canonical => 1});
return $s->encode($_[0]);
}
BEGIN {
if (eval 'use YAML::XS 0.67;1') {
*_yaml_dump = sub { local $YAML::XS::Boolean = 'JSON::PP'; YAML::XS::Dump(@_) };
*_yaml_load = sub { local $YAML::XS::Boolean = 'JSON::PP'; YAML::XS::Load(@_) };
}
else {
require YAML::PP;
my $pp = YAML::PP->new(boolean => 'JSON::PP');
*_yaml_dump = sub { $pp->dump_string(@_) };
*_yaml_load = sub { $pp->load_string(@_) };
}
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::Util - Utility functions for JSON::Validator
=head1 DESCRIPTION
L is a package containing utility functions for
L. Each of the L can be imported.
=head1 FUNCTIONS
=head2 data_checksum
$str = data_checksum $any;
Will create a checksum for any data structure stored in C<$any>.
=head2 data_section
$str = data_section "Some::Module", "file.json";
$str = data_section "Some::Module", "file.json", {encode => 'UTF-8'};
Same as L, but will also look up the file in any
inherited class.
=head2 data_type
$str = data_type $any;
$str = data_type $any, [@schemas];
$str = data_type $any, [{type => "integer", ...}];
Returns the JSON type for C<$any>. C<$str> can be array, boolean, integer,
null, number object or string. Note that a list of schemas need to be provided
to differentiate between "integer" and "number".
=head2 is_bool
$bool = is_bool $any;
Checks if C<$any> looks like a boolean.
=head2 is_num
$bool = is_num $any;
Checks if C<$any> looks like a number.
=head2 is_type
$bool = is_type $any, $class;
$bool = is_type $any, $type;
Checks if C<$any> is a, or inherits from, C<$class> or C<$type>.
=head2 negotiate_content_type
$content_type = negotiate_content_type($header, \@content_types);
This method can take a "Content-Type" or "Accept" header and find the closest
matching content type in C<@content_types>. C<@content_types> can contain
wildcards, meaning "*/*" will match anything.
=head2 prefix_errors
@errors = prefix_errors $prefix, @errors;
Consider this internal for now.
=head2 schema_type
$str = schema_type $hash_ref;
$str = schema_type $hash_ref, $any;
Looks at C<$hash_ref> and tries to figure out what kind of type the schema
represents. C<$str> can be "array", "const", "number", "object", "string", or
fallback to empty string if the correct type could not be figured out.
C<$any> can be provided to double check the type, so if C<$hash_ref> describes
an "object", but C<$any> is an array-ref, then C<$str> will become an empty
string. Example:
# $str = "";
$str = schema {additionalProperties => false}, [];
# $str = "object"
$str = schema {additionalProperties => false};
$str = schema {additionalProperties => false}, {};
Note that this process is relatively slow, so it will make your validation
faster if you specify "type". Both of the two below is valid, but the one with
"type" will be faster.
{"type": "object", "properties": {}} # Faster
{"properties": {}} # Slower
=head2 str2data
$any = str2data $str;
Will try to parse C<$str> as JSON or YAML, and return a data structure.
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema/ 0000755 0000765 0000024 00000000000 15211411467 020500 5 ustar jhthorsen staff JSON-Validator-5.18/lib/JSON/Validator/Schema/Draft4.pm 0000644 0000765 0000024 00000016413 15211411456 022165 0 ustar jhthorsen staff package JSON::Validator::Schema::Draft4;
use Mojo::Base 'JSON::Validator::Schema';
use JSON::Validator::Util qw(E data_checksum data_type is_type);
use List::Util 'uniq';
has id => sub {
my $data = shift->data;
return is_type($data, 'HASH') ? $data->{id} || '' : '';
};
has specification => 'http://json-schema.org/draft-04/schema#';
sub _build_formats {
return {
'date-time' => JSON::Validator::Formats->can('check_date_time'),
'email' => JSON::Validator::Formats->can('check_email'),
'hostname' => JSON::Validator::Formats->can('check_hostname'),
'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
'regex' => JSON::Validator::Formats->can('check_regex'),
'uri' => JSON::Validator::Formats->can('check_uri'),
};
}
sub _validate_number_max {
my ($self, $value, $state, $expected) = @_;
return unless defined(my $cmp_with = $state->{schema}{maximum});
my $key = $state->{schema}{exclusiveMaximum} ? 'ex_maximum' : 'maximum';
return if $key eq 'maximum' ? $value <= $cmp_with : $value < $cmp_with;
return E $state->{path}, [$expected => $key => $value, $cmp_with];
}
sub _validate_number_min {
my ($self, $value, $state, $expected) = @_;
return unless defined(my $cmp_with = $state->{schema}{minimum});
my $key = $state->{schema}{exclusiveMinimum} ? 'ex_minimum' : 'minimum';
return if $key eq 'minimum' ? $value >= $cmp_with : $value > $cmp_with;
return E $state->{path}, [$expected => $key => $value, $cmp_with];
}
sub _validate_type_array {
my ($self, $data, $state) = @_;
return E $state->{path}, [array => type => data_type $data] if ref $data ne 'ARRAY';
return (
$self->_validate_type_array_min_max($_[1], $state),
$self->_validate_type_array_unique($_[1], $state),
$self->_validate_type_array_items($_[1], $state),
);
}
sub _validate_type_array_items {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my @errors;
if (ref $schema->{items} eq 'ARRAY') {
my $additional_items = $schema->{additionalItems} // {};
my @rules = @{$schema->{items}};
if ($additional_items) {
push @rules, $additional_items while @rules < @$data;
}
if (@rules >= @$data) {
for my $i (0 .. @$data - 1) {
push @errors, $self->_validate($data->[$i], $self->_state($state, path => [@$path, $i], schema => $rules[$i]));
}
}
elsif (!$additional_items) {
push @errors, E $path, [array => additionalItems => int(@$data), int(@rules)];
}
}
elsif (exists $schema->{items}) {
for my $i (0 .. @$data - 1) {
push @errors,
$self->_validate($data->[$i], $self->_state($state, path => [@$path, $i], schema => $schema->{items}));
}
}
return @errors;
}
sub _validate_type_array_min_max {
my ($self, $data, $state) = @_;
my @errors;
if (defined $state->{schema}{minItems} and $state->{schema}{minItems} > @$data) {
push @errors, E $state->{path}, [array => minItems => int(@$data), $state->{schema}{minItems}];
}
if (defined $state->{schema}{maxItems} and $state->{schema}{maxItems} < @$data) {
push @errors, E $state->{path}, [array => maxItems => int(@$data), $state->{schema}{maxItems}];
}
return @errors;
}
sub _validate_type_array_unique {
my ($self, $data, $state) = @_;
return unless $state->{schema}{uniqueItems};
my (@errors, %uniq);
for (@$data) {
next if !$uniq{data_checksum($_)}++;
push @errors, E $state->{path}, [array => 'uniqueItems'];
last;
}
return @errors;
}
sub _validate_type_object {
my ($self, $data, $state) = @_;
return E $state->{path}, [object => type => data_type $data] if ref $data ne 'HASH';
return (
$self->_validate_type_object_min_max($_[1], $state),
$self->_validate_type_object_dependencies($_[1], $state),
$self->_validate_type_object_properties($_[1], $state),
);
}
sub _validate_type_object_min_max {
my ($self, $data, $state) = @_;
my @errors;
my @dkeys = keys %$data;
if (defined $state->{schema}{maxProperties} and $state->{schema}{maxProperties} < @dkeys) {
push @errors, E $state->{path}, [object => maxProperties => int(@dkeys), $state->{schema}{maxProperties}];
}
if (defined $state->{schema}{minProperties} and $state->{schema}{minProperties} > @dkeys) {
push @errors, E $state->{path}, [object => minProperties => int(@dkeys), $state->{schema}{minProperties}];
}
return @errors;
}
sub _validate_type_object_dependencies {
my ($self, $data, $state) = @_;
my $dependencies = $state->{schema}{dependencies} || {};
my @errors;
for my $k (keys %$dependencies) {
next if not exists $data->{$k};
if (ref $dependencies->{$k} eq 'ARRAY') {
push @errors,
map { E [@{$state->{path}}, $_], [object => dependencies => $k] }
grep { !exists $data->{$_} } @{$dependencies->{$k}};
}
else {
push @errors, $self->_validate($data, $self->_state($state, schema => $dependencies->{$k}));
}
}
return @errors;
}
sub _validate_type_object_properties {
my ($self, $data, $state) = @_;
my ($path, $schema, @errors, %rules) = @$state{qw(path schema)};
my $defaults = $self->{coerce}{defaults};
my @dkeys = keys %$data;
if (my $properties = $schema->{properties}) {
if ($defaults) {
push @{$rules{$_}}, $properties->{$_} for keys %$properties;
}
else {
defined $properties->{$_} && push @{$rules{$_}}, $properties->{$_} for @dkeys;
}
}
for my $p (keys %{$schema->{patternProperties} || {}}) {
my $r = $schema->{patternProperties}{$p};
push @{$rules{$_}}, $r for grep /$p/, @dkeys;
}
my $additional = exists $schema->{additionalProperties} ? $schema->{additionalProperties} : {};
if ($additional) {
$additional = {} unless is_type $additional, 'HASH';
$rules{$_} ||= [$additional] for @dkeys;
}
elsif (my @k = grep { !$rules{$_} } @dkeys) {
local $" = ', ';
return E $path, [object => additionalProperties => join ', ', sort @k];
}
for my $k (uniq @{$schema->{required} || []}) {
next if exists $data->{$k};
push @errors, E [@$path, $k], [object => 'required'];
delete $rules{$k};
}
for my $k (keys %rules) {
for my $r (@{$rules{$k}}) {
next if !exists $data->{$k} and !$defaults;
my $s2 = $self->_state($state, path => [@$path, $k], schema => $r);
if ($defaults and ref $s2->{schema} eq 'HASH' and exists $s2->{schema}{default} and !exists $data->{$k}) {
$data->{$k} = $s2->{schema}{default};
}
next if !exists $data->{$k};
my @e = $self->_validate($data->{$k}, $s2);
push @errors, @e;
next if @e or !is_type $r, 'HASH';
push @errors, $self->_validate_type_enum($data->{$k}, $s2) if $r->{enum};
push @errors, $self->_validate_type_const($data->{$k}, $s2) if $r->{const};
}
}
return @errors;
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema::Draft4 - JSON-Schema Draft 4
=head1 SYNOPSIS
See L.
=head1 DESCRIPTION
This class represents
L.
=head1 ATTRIBUTES
=head2 specification
my $str = $schema->specification;
my $schema = $schema->specification($str);
Defaults to "L".
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema/Draft201909.pm 0000644 0000765 0000024 00000015032 15211411456 022562 0 ustar jhthorsen staff package JSON::Validator::Schema::Draft201909;
use Mojo::Base 'JSON::Validator::Schema';
use JSON::Validator::Schema::Draft4;
use JSON::Validator::Schema::Draft6;
use JSON::Validator::Schema::Draft7;
use JSON::Validator::URI qw(uri);
use JSON::Validator::Util qw(E is_bool is_type);
has moniker => 'draft2019';
has specification => 'https://json-schema.org/draft/2019-09/schema';
has _ref_keys => sub { [qw($ref $recursiveRef)] };
sub _build_formats {
my $formats = shift->JSON::Validator::Schema::Draft7::_build_formats;
$formats->{duration} = JSON::Validator::Formats->can('check_duration');
$formats->{uuid} = JSON::Validator::Formats->can('check_uuid');
return $formats;
}
sub _bundle_ref_path_expand { local $_ = $_[1]; s!^\$defs/!!; return '$defs', $_; }
sub _extract_ref_from_schema { $_[1]->{'$recursiveRef'} // $_[1]->{'$ref'} }
sub _resolve_object {
my ($self, $state, $schema, $refs, $found) = @_;
if ($schema->{'$id'} and !ref $schema->{'$id'}) {
my $id = uri $schema->{'$id'}, $state->{base_url};
$self->store->add($id => $schema);
$state = {%$state}; # make sure we don't mutate $state ref
$state->{base_url} = $id->clone->fragment(undef);
}
if ($schema->{'$anchor'} && !ref $schema->{'$anchor'}) {
my $id = uri(uri()->new->fragment($schema->{'$anchor'}), $state->{base_url});
$self->store->add($id => $schema);
$state = {%$state, base_url => $id->fragment(undef)->to_string};
}
if ($found->{'$recursiveRef'} = $schema->{'$recursiveRef'} && !ref $schema->{'$recursiveRef'}) {
push @$refs, [$schema, $state];
}
if ($found->{'$ref'} = $schema->{'$ref'} && !ref $schema->{'$ref'}) {
push @$refs, [$schema, $state];
}
return $state;
}
sub _state {
my ($self, $curr, %override) = @_;
my $schema = $override{schema};
my (%alongside, %seen);
while (ref $schema eq 'HASH') {
last unless my $ref = $schema->{'$ref'} || $schema->{'$recursiveRef'};
last if ref $ref;
last if $seen{$schema}++;
%alongside = (%alongside, %$schema);
$schema = $self->_refs->{$schema}{schema}
// Carp::confess(qq(You have to call resolve() before validate() to lookup "$ref".));
}
return {%$curr, %override, schema => $schema} unless ref $schema eq 'HASH';
delete $alongside{$_} for qw($anchor $id $recursiveAnchor $recursiveRef $ref);
return {%$curr, %override, schema => {%alongside, %$schema}};
}
sub _validate_type_array_contains {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
return unless exists $schema->{contains};
return if defined $schema->{minContains} and $schema->{minContains} == 0 and !$schema->{maxContains};
return if defined $schema->{minContains} and $schema->{minContains} == 0 and !@$data;
my ($n_valid, @e, @errors) = (0);
for my $i (0 .. @$data - 1) {
my @tmp = $self->_validate($data->[$i], $self->_state($state, path => [@$path, $i], schema => $schema->{contains}));
@tmp ? push @e, \@tmp : $n_valid++;
}
push @errors, map {@$_} @e if @e >= @$data;
push @errors, E $path, [array => 'maxContains', int @$data, $schema->{maxContains}]
if exists $schema->{maxContains} and $n_valid > $schema->{maxContains};
push @errors, E $path, [array => 'minContains', int @$data, $schema->{minContains}]
if $schema->{minContains} and $n_valid < $schema->{minContains};
push @errors, E $path, [array => 'contains'] if not @$data;
return @errors;
}
sub _validate_type_object_dependencies {
my ($self, $data, $state) = @_;
my $dependencies = $state->{schema}{dependentSchemas} || {};
my @errors;
for my $k (keys %$dependencies) {
next if not exists $data->{$k};
if (ref $dependencies->{$k} eq 'ARRAY') {
push @errors,
map { E [@{$state->{path}}, $_], [object => dependencies => $k] }
grep { !exists $data->{$_} } @{$dependencies->{$k}};
}
else {
push @errors, $self->_validate($data, $self->_state($state, schema => $dependencies->{$k}));
}
}
$dependencies = $state->{schema}{dependentRequired} || {};
for my $k (keys %$dependencies) {
next if not exists $data->{$k};
push @errors,
map { E [@{$state->{path}}, $_], [object => dependencies => $k] }
grep { !exists $data->{$_} } @{$dependencies->{$k}};
}
return @errors;
}
*_validate_number_max = \&JSON::Validator::Schema::Draft6::_validate_number_max;
*_validate_number_min = \&JSON::Validator::Schema::Draft6::_validate_number_min;
*_validate_type_array = \&JSON::Validator::Schema::Draft6::_validate_type_array;
*_validate_type_array_items = \&JSON::Validator::Schema::Draft4::_validate_type_array_items;
*_validate_type_array_min_max = \&JSON::Validator::Schema::Draft4::_validate_type_array_min_max;
*_validate_type_array_unique = \&JSON::Validator::Schema::Draft4::_validate_type_array_unique;
*_validate_type_object = \&JSON::Validator::Schema::Draft6::_validate_type_object;
*_validate_type_object_min_max = \&JSON::Validator::Schema::Draft4::_validate_type_object_min_max;
*_validate_type_object_names = \&JSON::Validator::Schema::Draft6::_validate_type_object_names;
*_validate_type_object_properties = \&JSON::Validator::Schema::Draft4::_validate_type_object_properties;
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema::Draft201909 - JSON-Schema Draft 2019-09
=head1 SYNOPSIS
See L.
=head1 DESCRIPTION
This class represents
L.
Support for parsing the draft is not yet complete. Look at
L
for the most recent overview of what is not yet supported.
Currently less than 1% of the official test suite gets skipped. Here is a list of known
limitations:
=over 2
=item * Float and integers are equal up to 64-bit representation limits
This module is unable to say that the 64-bit number "9007199254740992.0" is the
same as "9007199254740992".
=item * unevaluatedItems
See L
=item * unevaluatedProperties
L and L needs to track what has been
validated or not using annotations. This is not yet supported.
=item * $recursiveAnchor
Basic support for C<$recursiveRef> is supported, but using it together with
C<$recursiveAnchor> is not.
=back
=head1 ATTRIBUTES
=head2 specification
my $str = $schema->specification;
Defaults to "L".
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema/Draft7.pm 0000644 0000765 0000024 00000006700 15211411456 022166 0 ustar jhthorsen staff package JSON::Validator::Schema::Draft7;
use Mojo::Base 'JSON::Validator::Schema';
use JSON::Validator::Schema::Draft4;
use JSON::Validator::Schema::Draft6;
use JSON::Validator::Util qw(E is_type);
has id => sub {
my $data = shift->data;
return is_type($data, 'HASH') ? $data->{'$id'} || '' : '';
};
has specification => 'http://json-schema.org/draft-07/schema#';
sub _build_formats {
return {
'date' => JSON::Validator::Formats->can('check_date'),
'date-time' => JSON::Validator::Formats->can('check_date_time'),
'email' => JSON::Validator::Formats->can('check_email'),
'hostname' => JSON::Validator::Formats->can('check_hostname'),
'idn-email' => JSON::Validator::Formats->can('check_idn_email'),
'idn-hostname' => JSON::Validator::Formats->can('check_idn_hostname'),
'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
'iri' => JSON::Validator::Formats->can('check_iri'),
'iri-reference' => JSON::Validator::Formats->can('check_iri_reference'),
'json-pointer' => JSON::Validator::Formats->can('check_json_pointer'),
'regex' => JSON::Validator::Formats->can('check_regex'),
'relative-json-pointer' => JSON::Validator::Formats->can('check_relative_json_pointer'),
'time' => JSON::Validator::Formats->can('check_time'),
'uri' => JSON::Validator::Formats->can('check_uri'),
'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
'uri-template' => JSON::Validator::Formats->can('check_uri_template'),
};
}
*_resolve_object = \&JSON::Validator::Schema::Draft6::_resolve_object;
*_validate_number_max = \&JSON::Validator::Schema::Draft6::_validate_number_max;
*_validate_number_min = \&JSON::Validator::Schema::Draft6::_validate_number_min;
*_validate_type_array = \&JSON::Validator::Schema::Draft6::_validate_type_array;
*_validate_type_array_contains = \&JSON::Validator::Schema::Draft6::_validate_type_array_contains;
*_validate_type_array_items = \&JSON::Validator::Schema::Draft4::_validate_type_array_items;
*_validate_type_array_min_max = \&JSON::Validator::Schema::Draft4::_validate_type_array_min_max;
*_validate_type_array_unique = \&JSON::Validator::Schema::Draft4::_validate_type_array_unique;
*_validate_type_object = \&JSON::Validator::Schema::Draft6::_validate_type_object;
*_validate_type_object_dependencies = \&JSON::Validator::Schema::Draft4::_validate_type_object_dependencies;
*_validate_type_object_min_max = \&JSON::Validator::Schema::Draft4::_validate_type_object_min_max;
*_validate_type_object_names = \&JSON::Validator::Schema::Draft6::_validate_type_object_names;
*_validate_type_object_properties = \&JSON::Validator::Schema::Draft4::_validate_type_object_properties;
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema::Draft7 - JSON-Schema Draft 7
=head1 SYNOPSIS
See L.
=head1 DESCRIPTION
This class represents
L.
=head1 ATTRIBUTES
=head2 specification
my $str = $schema->specification;
Defaults to "L".
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema/OpenAPIv2.pm 0000644 0000765 0000024 00000051403 15211411456 022542 0 ustar jhthorsen staff package JSON::Validator::Schema::OpenAPIv2;
use Mojo::Base 'JSON::Validator::Schema::Draft4';
use JSON::Validator::Util qw(E data_type negotiate_content_type schema_type);
use Mojo::Collection;
my $X_RE = qr{^x-};
# Some of the keywords are OpenAPIv3 keywords
my %SKIP_KEYWORDS_IN_PATH = map { ($_, 1) } qw(description parameters servers summary);
has errors => sub {
my $self = shift;
my $validator = $self->new(store => $self->store, _refs => {})->coerce('numbers,strings');
return [$validator->resolve($self->specification)->validate($self->resolve->data)];
};
has moniker => 'openapiv2';
has specification => 'http://swagger.io/v2/schema.json';
sub add_default_response {
my ($self, $params) = ($_[0], shift->_params_for_add_default_response(@_));
my $definitions = $self->data->{definitions} ||= {};
$definitions->{$params->{name}} ||= $params->{schema};
my $ref = {'$ref' => sprintf '%s#/definitions/%s', $self->id, $params->{name}};
$self->_register_ref($ref, schema => $definitions->{$params->{name}});
for my $route ($self->routes->each) {
my $op = $self->get([paths => @$route{qw(path method)}]);
for my $status (@{$params->{status}}) {
$op->{responses}{$status} ||= {description => $params->{description}, schema => $ref};
}
}
return $self;
}
sub base_url {
my ($self, $url) = @_;
my $spec = $self->data;
# Get
unless ($url) {
$url = Mojo::URL->new;
my @host_port = split ':', ($spec->{host} // '');
$url->host($host_port[0]) if $host_port[0];
$url->port($host_port[1]) if $host_port[1];
$url->path($spec->{basePath} || '/');
$url->scheme($spec->{schemes} && $spec->{schemes}[0] || undef);
$url->host('localhost') if $url->scheme and !$url->host;
return $url;
}
# Set
$url = Mojo::URL->new($url)->to_abs($self->base_url);
$spec->{host} = $url->host_port if $url->host_port;
$spec->{schemes}[0] = $url->scheme if $url->scheme;
$spec->{basePath} = $url->path->to_string || '/';
return $self;
}
sub coerce {
my $self = shift;
return $self->SUPER::coerce(@_) if @_;
$self->{coerce} ||= {booleans => 1, numbers => 1, strings => 1};
return $self->{coerce};
}
sub new {
my $self = shift->SUPER::new(@_);
$self->coerce; # make sure this attribute is built
$self;
}
sub parameters_for_request {
my $self = shift;
my ($method, $path) = (lc $_[0][0], $_[0][1]);
my $cache_key = "parameters_for_request:$method:$path";
return $self->{cache}{$cache_key} if $self->{cache}{$cache_key};
return undef unless $self->get([paths => $path, $method]);
my @accepts = map {@$_} $self->_find_all_nodes([paths => $path, $method], 'consumes');
my @parameters;
for my $param (map {@$_} $self->_find_all_nodes([paths => $path, $method], 'parameters')) {
push @parameters, {%$param};
$parameters[-1]{type} ||= schema_type($param->{schema} || $param);
$parameters[-1]{accepts} = \@accepts if $param->{in} eq 'body';
}
return $self->{cache}{$cache_key} = \@parameters;
}
sub parameters_for_response {
my $self = shift;
my ($method, $path, $status) = (lc $_[0][0], $_[0][1], $_[0][2] || 200);
$status ||= 200;
my $cache_key = "parameters_for_response:$method:$path:$status";
return $self->{cache}{$cache_key} if $self->{cache}{$cache_key};
my $responses = $self->get([paths => $path, $method, 'responses']);
my $response = $responses->{$status} || $responses->{default};
return undef unless $response;
my @parameters;
if (my $headers = $response->{headers}) {
push @parameters, map { +{%{$headers->{$_}}, in => 'header', name => $_} } sort keys %$headers;
}
my @accepts = $self->_find_all_nodes([paths => $path, $method], 'produces');
if (exists $response->{schema}) {
push @parameters, {%$response, in => 'body', name => 'body', accepts => pop @accepts || []};
}
return $self->{cache}{$cache_key} = \@parameters;
}
sub routes {
my $self = shift;
my @paths_with_weight;
for my $path (keys %{$self->get('/paths') || {}}) {
next if $path =~ $X_RE;
my $weight = '1';
$weight .= /^\{/ ? 0 : 1 for split '/', $path;
push @paths_with_weight, [int $weight, $path];
}
my @operations;
for (sort { $b->[0] <=> $a->[0] || $a->[1] cmp $b->[1] } @paths_with_weight) {
my $path = $_->[1];
next unless my $path_item = $self->get([paths => $path]);
$path_item = $self->get($path_item->{'$ref'} =~ s!^#!!r) if $path_item->{'$ref'};
for my $method (sort keys %$path_item) {
next if $method =~ $X_RE or $SKIP_KEYWORDS_IN_PATH{$method};
push @operations, {method => $method, operation_id => $path_item->{$method}{operationId}, path => $path};
}
}
return Mojo::Collection->new(@operations);
}
sub validate_request {
my ($self, $method_path, $req) = @_;
my $parameters = $self->parameters_for_request($method_path);
my %get;
for my $in (qw(body cookie formData header path query)) {
$get{$in} = ref $req->{$in} eq 'CODE' ? $req->{$in} : sub {
my ($name, $param) = @_;
return {exists => exists $req->{$in}, value => $req->{$in}} unless defined $name;
return {exists => exists $req->{$in}{$name}, value => $req->{$in}{$name}};
};
}
return $self->_validate_request_or_response(request => $parameters, \%get);
}
sub validate_response {
my ($self, $method_path_status, $res) = @_;
my $parameters = $self->parameters_for_response($method_path_status);
my %get;
for my $in (qw(body cookie header)) {
$get{$in} = ref $res->{$in} eq 'CODE' ? $res->{$in} : sub {
my ($name, $param) = @_;
return {exists => exists $res->{$in}{$name}, value => $res->{$in}{$name}};
};
}
return $self->_validate_request_or_response(response => $parameters, \%get);
}
sub _build_formats {
my $self = shift;
return {
'binary' => sub {undef},
'byte' => JSON::Validator::Formats->can('check_byte'),
'date' => JSON::Validator::Formats->can('check_date'),
'date-time' => JSON::Validator::Formats->can('check_date_time'),
'double' => JSON::Validator::Formats->can('check_double'),
'email' => JSON::Validator::Formats->can('check_email'),
'float' => JSON::Validator::Formats->can('check_float'),
'hostname' => JSON::Validator::Formats->can('check_hostname'),
'int32' => JSON::Validator::Formats->can('check_int32'),
'int64' => JSON::Validator::Formats->can('check_int64'),
'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
'password' => sub {undef},
'regex' => JSON::Validator::Formats->can('check_regex'),
'uri' => JSON::Validator::Formats->can('check_uri'),
};
}
sub _bundle_ref {
my ($self, $state, $source, $target) = @_;
# Recurse into the inner $ref to avoid invalid schema
my $source_state = $self->store->resolve($source->{'$ref'}, $state);
return $self->_bundle_ref($source_state, $source_state->{schema}, $target) if $source_state->{schema}{'$ref'};
# Replace "paths" inline
if (@{$state->{schema_path}} == 2 and $state->{schema_path}[0] eq 'paths') {
delete $state->{seen_schema}{$target}; # path definitions should not recurse
return [$source_state, $source_state->{schema}, $target, $state->{schema_path}];
}
return $self->SUPER::_bundle_ref($state, $source, $target);
}
sub _bundle_ref_path_expand {
my ($self, $state, $ref) = @_;
return (parameters => $ref) if $state->{schema}{in};
return ($1 => $2) if $ref =~ m!\b(definitions|parameters|responses)\b\.*(?:json|yaml)?(.+)$!;
return ($1, $2 ? ($2) : ()) if $ref =~ m!\b(x-[^/]+)/?\b(.*)!;
return (definitions => $ref);
}
sub _coerce_arrays {
my ($self, $val, $param) = @_;
my $data_type = data_type $val->{value};
my $schema = $param->{schema} || $param; # $param->{schema} is for OpenAPIv3
$schema = $self->get($schema->{'$ref'} =~ s!^#!!r) if $schema->{'$ref'};
my $schema_type = schema_type $schema;
return $val->{value} = [$val->{value}] if $schema_type eq 'array' and $data_type ne 'array';
return $val->{value} = @{$val->{value}} ? $val->{value}[-1] : undef
if $schema_type ne 'array' and $data_type eq 'array';
}
sub _coerce_default_value {
my ($self, $val, $param) = @_;
if ($param->{schema} and exists $param->{schema}{default}) {
@$val{qw(exists value)} = (1, $param->{schema}{default});
}
elsif (exists $param->{default}) {
@$val{qw(exists value)} = (1, $param->{default});
}
}
sub _coerce_parameter_format {
my ($self, $val, $param) = @_;
return unless $val->{exists};
return unless my $format = $param->{collectionFormat} || ($param->{type} eq 'array' ? 'csv' : '');
return $val->{value} = ref $val->{value} eq 'ARRAY' ? $val->{value} : [$val->{value}] if $format eq 'multi';
$val->{value} = $val->{value}[0] // '' if ref $val->{value} eq 'ARRAY';
return $val->{value} = [split /\|/, $val->{value}] if $format eq 'pipes';
return $val->{value} = [split /[ ]/, $val->{value}] if $format eq 'ssv';
return $val->{value} = [split /\t/, $val->{value}] if $format eq 'tsv';
return $val->{value} = [split /,/, $val->{value}];
}
sub _default_response_schema {
return {
type => 'object',
required => ['errors'],
properties => {
errors => {
type => 'array',
items => {
type => 'object',
required => ['message'],
properties => {message => {type => 'string'}, path => {type => 'string'}},
},
},
},
};
}
sub _extract_ref_from_schema { $_[1]->{'$ref'} =~ /^\w+$/ ? "#/definitions/$_[1]->{'$ref'}" : $_[1]->{'$ref'} }
sub _find_all_nodes {
my ($self, $pointer, $leaf) = @_;
my @found;
push @found, $self->data->{$leaf} if ref $self->data->{$leaf} eq 'ARRAY';
my @path;
for my $p (@$pointer) {
push @path, $p;
my $node = $self->get([@path]);
push @found, $node->{$leaf} if ref $node->{$leaf} eq 'ARRAY';
}
# Resolve all $ref before returning the data
for my $items (@found) {
for my $schema (@$items) {
my %seen;
while (ref $schema eq 'HASH' and $schema->{'$ref'} and !ref $schema->{'$ref'}) {
last if $seen{$schema}++;
$schema = $self->_refs->{$schema}{schema}
// Carp::confess(qq(resolve() must be called before validate() to lookup "$schema->{'$ref'}".));
}
}
}
return @found;
}
sub _get_parameter_value {
my ($self, $param, $get) = @_;
my $val = $get->{$param->{in}}->($param->{name}, $param);
@$val{qw(in name)} = (@$param{qw(in name)});
return $val;
}
sub _params_for_add_default_response {
my $self = shift;
my $params = shift || {};
return {
description => $params->{description} || 'Default response.',
name => $params->{name} || 'DefaultResponse',
schema => $params->{schema} || _default_response_schema(),
status => $params->{status} || [400, 401, 404, 500, 501],
};
}
sub _prefix_error_path {
return join '', "/$_[0]", $_[1] =~ /\w/ ? ($_[1]) : ();
}
sub _validate_body {
my ($self, $direction, $val, $param) = @_;
if ($val->{accept}) {
$val->{content_type} = negotiate_content_type($param->{accepts}, $val->{accept});
$val->{valid} = $val->{content_type} ? 1 : 0;
return E "/header/Accept", [join(', ', @{$param->{accepts}}), type => $val->{accept}] unless $val->{valid};
}
if (@{$param->{accepts}} and $val->{content_type}) {
my $negotiated = negotiate_content_type($param->{accepts}, $val->{content_type});
$val->{valid} = $negotiated ? 1 : 0;
return E "/$param->{name}", [join(', ', @{$param->{accepts}}) => type => $val->{content_type}] unless $negotiated;
}
if ($param->{required} and !$val->{exists}) {
$val->{valid} = 0;
return E "/$param->{name}", [qw(object required)];
}
if ($val->{exists}) {
local $self->{"validate_$direction"} = 1;
$val->{content_type} //= $param->{accepts}[0];
my @errors = map { $_->path(_prefix_error_path($param->{name}, $_->path)); $_ }
$self->validate($val->{value}, $param->{schema});
$val->{valid} = @errors ? 0 : 1;
return @errors;
}
return;
}
sub _validate_id { }
sub _validate_request_or_response {
my ($self, $direction, $parameters, $get) = @_;
my @errors;
for my $param (@$parameters) {
my $val = $self->_get_parameter_value($param, $get);
$self->_coerce_parameter_format($val, $param) if $direction eq 'request' and $param->{in} ne 'body';
$self->_coerce_default_value($val, $param) unless $val->{exists};
if ($param->{in} eq 'body') {
push @errors, $self->_validate_body($direction, $val, $param);
next;
}
if ($val->{exists}) {
$self->_coerce_arrays($val, $param);
local $self->{"validate_$direction"} = 1;
my @e = map { $_->path(_prefix_error_path($param->{name}, $_->path)); $_ }
$self->validate($val->{value}, $param->{schema} || $param);
push @errors, @e;
$val->{valid} = @e ? 0 : 1;
}
elsif ($param->{required}) {
push @errors, E "/$param->{name}", [qw(object required)];
$val->{valid} = 0;
}
}
return @errors;
}
sub _validate_type_file {
my ($self, $data, $state) = @_;
return unless $state->{schema}{required} and (not defined $data or not length $data);
return E $state->{path}, 'Missing property.';
}
sub _validate_type_object {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
return E $path, [object => type => data_type $data] if ref $data ne 'HASH';
return shift->SUPER::_validate_type_object(@_) unless $self->{validate_request};
my (@errors, %ro);
for my $name (keys %{$schema->{properties} || {}}) {
next unless $self->_get(['readOnly'], {path => [], schema => $schema->{properties}{$name}});
push @errors, E [@$path, $name], "Read-only." if exists $data->{$name};
$ro{$name} = 1;
}
local $schema->{required} = [grep { !$ro{$_} } @{$schema->{required} || []}];
my $discriminator = $schema->{discriminator};
if ($discriminator and !$self->{inside_discriminator}) {
return E $path, "Discriminator $discriminator has no value." unless my $name = $data->{$discriminator};
return E $path, "No definition for discriminator $name." unless my $ds = $self->get("/definitions/$name");
local $self->{inside_discriminator} = 1;
return $self->_validate($data, $self->_state($state, schema => $ds));
}
return (
@errors,
$self->_validate_type_object_min_max($_[1], $state),
$self->_validate_type_object_dependencies($_[1], $state),
$self->_validate_type_object_properties($_[1], $state),
);
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema::OpenAPIv2 - OpenAPI version 2 / Swagger
=head1 SYNOPSIS
use JSON::Validator;
my $schema = JSON::Validator->new->schema("...")->schema;
# Check for specification errors
my $errors = $schema->errors;
# Returns a list of zero or more JSON::Validator::Error objects
my @request_errors = $schema->validate_request(
[get => "/path"],
{body => sub { return {exists => 1, value => {}} }},
);
# Returns a list of zero or more JSON::Validator::Error objects
my @response_errors = $schema->validate_response(
[get => "/path", 200],
{body => sub { return {exists => 1, value => {}} }},
);
=head1 DESCRIPTION
This class represents L.
=head1 ATTRIBUTES
=head2 errors
my $array_ref = $schema->errors;
See L.
=head2 moniker
$str = $schema->moniker;
$schema = $schema->moniker("openapiv2");
Used to get/set the moniker for the given schema. Default value is "openapiv2".
=head2 specification
my $str = $schema->specification;
my $schema = $schema->specification($str);
Defaults to "L".
=head1 METHODS
=head2 add_default_response
$schema = $schema->add_default_response(\%params);
Used to add a default response schema for operations that does not already have
one. C<%params> can be:
=over 2
=item * description
The human readable description added to the operation.
Defaults: "Default response."
=item * name
The name used in the specification under "/components/schemas/".
Defaults: "DefaultResponse"
=item * schema
The schema to add. The default schema below might change, but the basics will
stay the same:
{
type: "object",
required: ["errors"],
properties: {
errors: {
type: "array",
items: {
type: "object",
required: ["message"],
properties: {
message: {type: "string"},
path: {type: "string"}
}
}
}
}
}
=item * status
A list of status codes to apply the default schema to.
Default: C<[400, 401, 404, 500, 501]>.
=back
=head2 base_url
$url = $schema->base_url;
$schema = $schema->base_url($url);
Can get or set the default URL for this schema. C<$url> can be either a
L object or a plain string.
This method will read or write "basePath", "host" and/or "schemas" in L.
=head2 coerce
my $schema = $schema->coerce({booleans => 1, numbers => 1, strings => 1});
my $hash_ref = $schema->coerce;
Coercion is enabled by default, since headers, path parts, query parameters,
... are in most cases strings.
See also L.
=head2 new
$schema = JSON::Validator::Schema::OpenAPIv2->new(\%attrs);
$schema = JSON::Validator::Schema::OpenAPIv2->new;
Same as L, but will also build L/coerce>.
=head2 parameters_for_request
$parameters = $schema->parameters_for_request([$method, $path]);
Finds all the request parameters defined in the schema, including inherited
parameters. Returns C if the C<$path> and C<$method> cannot be found.
Example return value:
[
{in => "query", name => "q"},
{in => "body", name => "body", accepts => ["application/json"]},
]
The return value MUST not be mutated.
=head2 parameters_for_response
$array_ref = $schema->parameters_for_response([$method, $path, $status]);
Finds the response parameters defined in the schema. Returns C if the
C<$path>, C<$method> and C<$status> cannot be found. Will default to the
"default" response definition if C<$status> could not be found and "default"
exists.
Example return value:
[
{in => "header", name => "X-Foo"},
{in => "body", name => "body", accepts => ["application/json"]},
]
The return value MUST not be mutated.
=head2 routes
$collection = $schema->routes;
Used to gather all available routes in the schema and return them sorted. The
result is a L object, where each item has a hash looking like
this:
{
method => 'get',
path => '/user/{id}',
operation_id => 'getUser', # Might be undef()
}
=head2 validate_request
@errors = $schema->validate_request([$method, $path], \%req);
This method can be used to validate a HTTP request. C<%req> should contain
key/value pairs representing the request parameters. Example:
%req = (
body => sub {
my ($name, $param) = shift;
# $param = {name => $name, in => ..., schema => ..., ...}
return {exists => 1, value => \%all_params} unless defined $name;
return {exists => 1, value => "..."};
},
formData => {email => "..."},
header => {"X-Request-Base" => "..."},
path => {id => "..."},
query => {limit => 42},
);
"formData", "header", "path" and "query" can be either a hash-ref, a hash-like
object or a code ref, while "body" MUST be a code ref. The return value from
the code ref will get mutated, making it possible to check if an individual
parameter was validated or not.
# Before: "exists" and "value" must be present
my @evaluated;
$req{query} = sub { push @evaluated, {exists => 1, value => 42}, return $evaluated[-1] };
# Validate
$schema->validate_request(get => "/user"], \%req);
# After: "in", "name" and "valid" are added
$evaluated[-1] ==> {exists => 1, value => 42, in => "query", name => "foo", valid => 1};
A plain hash-ref will I get mutated.
The body hash-ref can also have a "content_type" key. This will be checked
against the list of valid request or response content types in the spec.
=head2 validate_response
@errors = $schema->validate_response([$method, $path, $status], \%res);
This method can be used to validate a HTTP response. C<%res> should contain
key/value pairs representing the response parameters. Example:
%res = (
body => sub {
my ($name, $param) = shift;
# $param = {name => $name, in => ..., ...}
return {exists => 1, value => \%all_params} unless defined $name;
return {accept => "application/json", exists => 1, value => "..."};
},
header => {"Location" => "..."},
);
C<%res> follows the same rules as C<%req> in L, but also
supports "accept", instead of specifying "content_type". "accept" should have
the same format as an "Accept" HTTP header.
=head1 SEE ALSO
L, L,
L
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema/OpenAPIv3.pm 0000644 0000765 0000024 00000046064 15211411456 022552 0 ustar jhthorsen staff package JSON::Validator::Schema::OpenAPIv3;
use Mojo::Base 'JSON::Validator::Schema::Draft201909';
use JSON::Validator::Util qw(E data_type negotiate_content_type schema_type);
use Mojo::JSON qw(false true);
use Mojo::Path;
has moniker => 'openapiv3';
has specification => 'https://spec.openapis.org/oas/3.0/schema/2021-09-28';
require JSON::Validator::Schema::OpenAPIv2;
*coerce = \&JSON::Validator::Schema::OpenAPIv2::coerce;
*errors = \&JSON::Validator::Schema::OpenAPIv2::errors;
*routes = \&JSON::Validator::Schema::OpenAPIv2::routes;
*validate_request = \&JSON::Validator::Schema::OpenAPIv2::validate_request;
*validate_response = \&JSON::Validator::Schema::OpenAPIv2::validate_response;
*_coerce_arrays = \&JSON::Validator::Schema::OpenAPIv2::_coerce_arrays;
*_coerce_default_value = \&JSON::Validator::Schema::OpenAPIv2::_coerce_default_value;
*_find_all_nodes = \&JSON::Validator::Schema::OpenAPIv2::_find_all_nodes;
*_params_for_add_default_response = \&JSON::Validator::Schema::OpenAPIv2::_params_for_add_default_response;
*_prefix_error_path = \&JSON::Validator::Schema::OpenAPIv2::_prefix_error_path;
*_validate_request_or_response = \&JSON::Validator::Schema::OpenAPIv2::_validate_request_or_response;
sub add_default_response {
my ($self, $params) = ($_[0], shift->_params_for_add_default_response(@_));
my $schemas = $self->data->{components}{schemas} ||= {};
$schemas->{$params->{name}} ||= $params->{schema};
my $ref = {'$ref' => "#/components/schemas/$params->{name}"};
$self->_register_ref($ref, schema => $schemas->{$params->{name}});
for my $route ($self->routes->each) {
my $op = $self->get([paths => @$route{qw(path method)}]);
for my $status (@{$params->{status}}) {
next if $self->get(['paths', @$route{qw(path method)}, 'responses', $status]);
$op->{responses}{$status}{content}{'application/json'} //= {schema => $ref};
$op->{responses}{$status}{description} //= $params->{description};
}
}
return $self;
}
sub base_url {
my ($self, $url) = @_;
# Get
return Mojo::URL->new($self->get('/servers/0/url') || '') unless $url;
# Set
$url = Mojo::URL->new($url)->to_abs($self->base_url);
$self->data->{servers}[0]{url} = $url->to_string;
return $self;
}
sub new {
my $self = shift->SUPER::new(@_);
$self->coerce; # make sure this attribute is built
$self;
}
sub parameters_for_request {
my $self = shift;
my ($method, $path) = (lc $_[0][0], $_[0][1]);
my $cache_key = "parameters_for_request:$method:$path";
return $self->{cache}{$cache_key} if $self->{cache}{$cache_key};
return undef unless $self->get([paths => $path, $method]);
my @parameters = map {@$_} $self->_find_all_nodes([paths => $path, $method], 'parameters');
if (my $request_body = $self->get([paths => $path, $method, 'requestBody'])) {
my @accepts = sort keys %{$request_body->{content} || {}};
push @parameters,
{
accepts => \@accepts,
content => $request_body->{content},
in => 'body',
name => 'body',
required => $request_body->{required},
};
}
return $self->{cache}{$cache_key} = \@parameters;
}
sub parameters_for_response {
my $self = shift;
my ($method, $path, $status) = (lc $_[0][0], $_[0][1], $_[0][2] || 200);
$status ||= 200;
my $cache_key = "parameters_for_response:$method:$path:$status";
return $self->{cache}{$cache_key} if $self->{cache}{$cache_key};
my $response = $self->get([paths => $path, $method, 'responses', $status])
|| $self->get([paths => $path, $method, 'responses', 'default']);
return undef unless $response;
my @parameters;
if (my $headers = $response->{headers}) {
push @parameters, map { +{%{$headers->{$_}}, in => 'header', name => $_} } sort keys %$headers;
}
if (my @accepts = sort keys %{$response->{content} || {}}) {
push @parameters, {accepts => \@accepts, content => $response->{content}, in => 'body', name => 'body'};
}
return $self->{cache}{$cache_key} = \@parameters;
}
sub _build_formats {
# TODO: Figure out if this is the correct list
return {
'binary' => sub {undef},
'byte' => JSON::Validator::Formats->can('check_byte'),
'date' => JSON::Validator::Formats->can('check_date'),
'date-time' => JSON::Validator::Formats->can('check_date_time'),
'double' => JSON::Validator::Formats->can('check_double'),
'duration' => JSON::Validator::Formats->can('check_duration'),
'email' => JSON::Validator::Formats->can('check_email'),
'float' => JSON::Validator::Formats->can('check_float'),
'hostname' => JSON::Validator::Formats->can('check_hostname'),
'idn-email' => JSON::Validator::Formats->can('check_idn_email'),
'idn-hostname' => JSON::Validator::Formats->can('check_idn_hostname'),
'int32' => JSON::Validator::Formats->can('check_int32'),
'int64' => JSON::Validator::Formats->can('check_int64'),
'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
'iri' => JSON::Validator::Formats->can('check_iri'),
'iri-reference' => JSON::Validator::Formats->can('check_iri_reference'),
'json-pointer' => JSON::Validator::Formats->can('check_json_pointer'),
'password' => sub {undef},
'regex' => JSON::Validator::Formats->can('check_regex'),
'relative-json-pointer' => JSON::Validator::Formats->can('check_relative_json_pointer'),
'time' => JSON::Validator::Formats->can('check_time'),
'uri' => JSON::Validator::Formats->can('check_uri'),
'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
'uri-template' => JSON::Validator::Formats->can('check_uri_template'),
'uuid' => JSON::Validator::Formats->can('check_uuid'),
};
}
sub _bundle_ref_path_expand {
my ($self, $schema, $ref) = @_;
return $ref =~ m!\bcomponents/([^/]+)/(.+)! ? ('components', $1, $2) : ('components', 'schemas', $ref);
}
sub _coerce_parameter_format {
my ($self, $val, $param) = @_;
return unless $val->{exists};
state $in_style = {cookie => 'form', header => 'simple', path => 'simple', query => 'form'};
$param->{style} = $in_style->{$param->{in}} unless $param->{style};
return $self->_coerce_parameter_style_object_deep($val, $param) if +($param->{style} // '') eq 'deepObject';
my $schema_type = schema_type $param->{schema};
return $self->_coerce_parameter_style_array($val, $param) if $schema_type eq 'array';
return $self->_coerce_parameter_style_object($val, $param) if $schema_type eq 'object';
}
sub _coerce_parameter_style_array {
my ($self, $val, $param) = @_;
my $style = $param->{style};
my $explode = $param->{explode} // $param->{style} eq 'form' ? true : false;
my $re;
if ($style =~ m!^(form|pipeDelimited|spaceDelimited|simple)$!) {
return $val->{value} = ref $val->{value} eq 'ARRAY' ? $val->{value} : [$val->{value}] if $explode;
$re = $style eq 'pipeDelimited' ? qr{\|} : $style eq 'spaceDelimited' ? $re = qr{[ ]} : qr{,};
}
elsif ($style eq 'label') {
$re = qr{\.};
$re = qr{,} if $val->{value} =~ s/^$re// and !$explode;
}
elsif ($style eq 'matrix') {
$re = qr{;\Q$param->{name}\E=};
$re = qr{,} if $val->{value} =~ s/^$re// and !$explode;
}
return $val->{value} = [_split($re, $val->{value})];
}
sub _coerce_parameter_style_object {
my ($self, $val, $param) = @_;
my $style = $param->{style};
my $explode = $param->{explode} // (grep { $style eq $_ } qw(cookie query)) ? 1 : 0;
if ($explode) {
return $self->_coerce_parameter_style_object_form($val, $param) if $style eq 'form';
state $style_re = {label => qr{\.}, matrix => qr{;}, simple => qr{,}};
return unless my $re = $style_re->{$style};
return if $style eq 'matrix' && $val->{value} !~ s/^;//;
return if $style eq 'label' && $val->{value} !~ s/^\.//;
my $params = Mojo::Parameters->new;
$params->append(Mojo::Parameters->new($_)) for _split($re, $val->{value});
return $val->{value} = $params->to_hash;
}
else {
state $style_re = {
form => qr{,},
label => qr{\.},
matrix => qr{,},
pipeDelimited => qr{\|},
simple => qr{,},
spaceDelimited => qr{[ ]},
};
return unless my $re = $style_re->{$style};
return if $style eq 'matrix' && $val->{value} !~ s/^;\Q$param->{name}\E=//;
return if $style eq 'label' && $val->{value} !~ s/^\.//;
return $val->{value} = Mojo::Parameters->new->pairs([_split($re, $val->{value})])->to_hash;
}
}
sub _coerce_parameter_style_object_deep {
my ($self, $val, $param) = @_;
my %res;
for my $k (keys %{$val->{value}}) {
next unless $k =~ /^\Q$param->{name}\E\[(.*)\]/;
my @path = $k =~ m!\[([^]]*)\]!g;
my $values = ref $val->{value}{$k} eq 'ARRAY' ? $val->{value}{$k} : [$val->{value}{$k}];
my $node = \%res;
while (defined(my $p = shift @path)) {
if (@path) {
my $next = $path[0] =~ m!^(|\d+)$! ? [] : {};
$node = ref $node eq 'ARRAY' ? ($node->[$p] ||= $next) : ($node->{$p} ||= $next);
}
elsif ($p eq '') {
@$node = @$values;
}
elsif ($p =~ /^\d+$/) {
$node->[$p] = $values->[0];
}
else {
$node->{$p} = @$values > 1 ? $values : $values->[0];
}
}
}
return $val->{value} = \%res if %res;
return $val->{exists} = 0;
}
sub _coerce_parameter_style_object_form {
my ($self, $val, $param) = @_;
return unless my $properties = $param->{schema} && $param->{schema}{properties};
for my $k (keys %{$val->{value}}) {
next unless my $type = $properties->{$k} && $properties->{$k}{type};
$val->{value}{$k} = [$val->{value}{$k}] if $type eq 'array' and ref $val->{value}{$k} ne 'ARRAY';
}
}
sub _get_parameter_value {
my ($self, $param, $get) = @_;
my $schema_type = schema_type $param->{schema};
my $name = $param->{name};
$name = undef if $schema_type eq 'object' && $param->{explode} && ($param->{style} || '') =~ m!^(form|deepObject)$!;
my $val = $get->{$param->{in}}->($name, $param);
@$val{qw(in name)} = (@$param{qw(in name)});
return $val;
}
sub _split {
my ($re, $val) = @_;
$val = @$val ? $val->[-1] : '' if ref $val;
return split /$re/, $val;
}
sub _to_list { ref $_[0] eq 'ARRAY' ? @{$_[0]} : $_[0] ? ($_[0]) : () }
sub _validate_body {
my ($self, $direction, $val, $param) = @_;
if ($val->{accept}) {
$val->{content_type} = negotiate_content_type($param->{accepts}, $val->{accept});
$val->{valid} = $val->{content_type} ? 1 : 0;
return E "/header/Accept", [join(', ', @{$param->{accepts}}), type => $val->{accept}] unless $val->{valid};
}
my $negotiated_content_type;
if (@{$param->{accepts}} and $val->{content_type}) {
$negotiated_content_type = negotiate_content_type($param->{accepts}, $val->{content_type});
$val->{valid} = $negotiated_content_type ? 1 : 0;
return E "/$param->{name}", [join(', ', @{$param->{accepts}}) => type => $val->{content_type}] unless $negotiated_content_type;
}
if ($param->{required} and !$val->{exists}) {
$val->{valid} = 0;
return E "/$param->{name}", [qw(object required)];
}
if ($val->{exists}) {
# Ensures we have a valid content-type so we can select a schema
# This can happen if the negotiation fails (e.g. content-type is empty)
$negotiated_content_type //= $param->{accepts}[0];
# Mutate request content-type if one was not set
$val->{content_type} //= $negotiated_content_type;
local $self->{coerce}{arrays} = 1
if $val->{content_type} =~ m!^(application/x-www-form-urlencoded|multipart/form-data)\s*(;|$)!;
local $self->{"validate_$direction"} = 1;
my $body_params = $param->{content}{$negotiated_content_type};
my @errors = map { $_->path(_prefix_error_path($param->{name}, $_->path)); $_ }
$self->validate($val->{value}, $body_params->{schema});
$val->{valid} = @errors ? 0 : 1;
return @errors;
}
return;
}
sub _validate_id { }
sub _validate_type_array {
my $self = shift;
$_[0] = [$_[0]] if ref $_[0] ne 'ARRAY' and $self->{coerce}{arrays};
return $_[1]->{schema}{nullable} && !defined $_[0] ? () : $self->SUPER::_validate_type_array(@_);
}
sub _validate_type_boolean {
my $self = shift;
return $_[1]->{schema}{nullable} && !defined $_[0] ? () : $self->SUPER::_validate_type_boolean(@_);
}
sub _validate_type_enum {
my $self = shift;
return $_[1]->{schema}{nullable} && !defined $_[0] ? () : $self->SUPER::_validate_type_enum(@_);
}
sub _validate_type_integer {
my $self = shift;
return $_[1]->{schema}{nullable} && !defined $_[0] ? () : $self->SUPER::_validate_type_integer(@_);
}
sub _validate_type_number {
my $self = shift;
return $_[1]->{schema}{nullable} && !defined $_[0] ? () : $self->SUPER::_validate_type_number(@_);
}
sub _validate_type_object {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
return if $schema->{nullable} && !defined $data;
return E $path, [object => type => data_type $data] if ref $data ne 'HASH';
return shift->SUPER::_validate_type_object(@_) unless $self->{validate_request} or $self->{validate_response};
# TODO: Support external URLs in "mapping"
my $discriminator = $schema->{discriminator};
if (ref $discriminator eq 'HASH' and $discriminator->{propertyName} and !$self->{inside_discriminator}) {
my ($name, $mapping) = @$discriminator{qw(propertyName mapping)};
return E $path, "Discriminator $name has no value." unless my $map_name = $data->{$name};
return E $path, "No definition for discriminator $map_name." unless my $url = $mapping->{$map_name};
return E $path, "TODO: Not yet supported: $url" unless $url =~ s!^#!!;
local $self->{inside_discriminator} = 1;
return $self->_validate($data, $self->_state($state, schema => $self->get($url)));
}
return $self->{validate_request}
? $self->_validate_type_object_request($_[1], $state)
: $self->_validate_type_object_response($_[1], $state);
}
sub _validate_type_object_request {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my (@errors, %ro);
for my $name (keys %{$schema->{properties} || {}}) {
next unless $self->_get(['readOnly'], {path => [], schema => $schema->{properties}{$name}});
push @errors, E [@$path, $name], "Read-only." if exists $data->{$name};
$ro{$name} = 1;
}
local $schema->{required} = [grep { !$ro{$_} } @{$schema->{required} || []}];
return (
@errors,
$self->_validate_type_object_min_max($_[1], $state),
$self->_validate_type_object_dependencies($_[1], $state),
$self->_validate_type_object_properties($_[1], $state),
);
}
sub _validate_type_object_response {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my (@errors, %rw);
for my $name (keys %{$schema->{properties} || {}}) {
next unless $self->_get(['writeOnly'], {path => [], schema => $schema->{properties}{$name}});
push @errors, E [@$path, $name], "Write-only." if exists $data->{$name};
$rw{$name} = 1;
}
local $schema->{required} = [grep { !$rw{$_} } @{$schema->{required} || []}];
return (
@errors,
$self->_validate_type_object_min_max($_[1], $state),
$self->_validate_type_object_dependencies($_[1], $state),
$self->_validate_type_object_properties($_[1], $state),
);
}
sub _validate_type_string {
my ($self, $val, $state) = @_;
return () if $state->{schema}{nullable} && !defined $val;
if (ref $val eq 'Mojo::Upload') {
return () if
defined $state->{schema}{format} &&
$state->{schema}{format} eq 'binary';
return E $state->{path}, [string => type => data_type $val];
}
return $self->SUPER::_validate_type_string($val, $state);
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema::OpenAPIv3 - OpenAPI version 3
=head1 SYNOPSIS
See L.
=head1 DESCRIPTION
This class represents L.
=head1 ATTRIBUTES
=head2 errors
my $array_ref = $schema->errors;
See L.
=head2 moniker
$str = $schema->moniker;
$schema = $schema->moniker("openapiv3");
Used to get/set the moniker for the given schema. Default value is "openapiv3".
=head2 specification
my $str = $schema->specification;
my $schema = $schema->specification($str);
Defaults to "L".
=head1 METHODS
=head2 add_default_response
$schema = $schema->add_default_response(\%params);
See L for details.
=head2 base_url
$url = $schema->base_url;
$schema = $schema->base_url($url);
Can get or set the default URL for this schema. C<$url> can be either a
L object or a plain string.
This method will read or write "/servers/0/url" in L.
=head2 coerce
my $schema = $schema->coerce({booleans => 1, numbers => 1, strings => 1});
my $hash_ref = $schema->coerce;
Coercion is enabled by default, since headers, path parts, query parameters,
... are in most cases strings.
=head2 new
$schema = JSON::Validator::Schema::OpenAPIv2->new(\%attrs);
$schema = JSON::Validator::Schema::OpenAPIv2->new;
Same as L, but will also build L/coerce>.
=head2 parameters_for_request
$parameters = $schema->parameters_for_request([$method, $path]);
Finds all the request parameters defined in the schema, including inherited
parameters. Returns C if the C<$path> and C<$method> cannot be found.
Example return value:
[
{in => "query", name => "q"},
{in => "body", name => "body", accepts => ["application/json"]},
]
The return value MUST not be mutated.
=head2 parameters_for_response
$array_ref = $schema->parameters_for_response([$method, $path, $status]);
Finds the response parameters defined in the schema. Returns C if the
C<$path>, C<$method> and C<$status> cannot be found. Will default to the
"default" response definition if C<$status> could not be found and "default"
exists.
Example return value:
[
{in => "header", name => "X-Foo"},
{in => "body", name => "body", accepts => ["application/json"]},
]
The return value MUST not be mutated.
=head2 routes
$collection = $schema->routes;
Shares the same interface as L.
=head2 validate_request
@errors = $schema->validate_request([$method, $path], \%req);
Shares the same interface as L.
=head2 validate_response
@errors = $schema->validate_response([$method, $path], \%req);
Shares the same interface as L.
=head1 SEE ALSO
L, L and and
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema/Draft6.pm 0000644 0000765 0000024 00000012303 15211411456 022161 0 ustar jhthorsen staff package JSON::Validator::Schema::Draft6;
use Mojo::Base 'JSON::Validator::Schema';
use JSON::Validator::Schema::Draft4;
use JSON::Validator::URI qw(uri);
use JSON::Validator::Util qw(E data_type is_type prefix_errors);
has id => sub {
my $data = shift->data;
return is_type($data, 'HASH') ? $data->{'$id'} || '' : '';
};
has specification => 'http://json-schema.org/draft-06/schema#';
sub _build_formats {
return {
'date-time' => JSON::Validator::Formats->can('check_date_time'),
'email' => JSON::Validator::Formats->can('check_email'),
'hostname' => JSON::Validator::Formats->can('check_hostname'),
'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
'json-pointer' => JSON::Validator::Formats->can('check_json_pointer'),
'regex' => JSON::Validator::Formats->can('check_regex'),
'relative-json-pointer' => JSON::Validator::Formats->can('check_relative_json_pointer'),
'uri' => JSON::Validator::Formats->can('check_uri'),
'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
'uri-template' => JSON::Validator::Formats->can('check_uri_template'),
};
}
sub _resolve_object {
my ($self, $state, $schema, $refs, $found) = @_;
if ($schema->{'$id'} and !ref $schema->{'$id'}) {
my $id = uri $schema->{'$id'}, $state->{base_url};
$self->store->add($id => $schema);
$state = {%$state, base_url => $id->fragment(undef)->to_string};
}
if ($found->{'$ref'} = $schema->{'$ref'} && !ref $schema->{'$ref'}) {
push @$refs, [$schema, $state];
}
return $state;
}
sub _validate_number_max {
my ($self, $value, $state, $expected) = @_;
my $cmp_with = $state->{schema}{maximum};
return E $state->{path}, [$expected => maximum => $value, $cmp_with] if defined $cmp_with and $value > $cmp_with;
$cmp_with = $state->{schema}{exclusiveMaximum};
return E $state->{path}, [$expected => ex_maximum => $value, $cmp_with] if defined $cmp_with and $value >= $cmp_with;
return;
}
sub _validate_number_min {
my ($self, $value, $state, $expected) = @_;
my $cmp_with = $state->{schema}{minimum};
return E $state->{path}, [$expected => minimum => $value, $cmp_with] if defined $cmp_with and $value < $cmp_with;
$cmp_with = $state->{schema}{exclusiveMinimum};
return E $state->{path}, [$expected => ex_minimum => $value, $cmp_with] if defined $cmp_with and $value <= $cmp_with;
return;
}
sub _validate_type_array {
my ($self, $data, $state) = @_;
return E $state->{path}, [array => type => data_type $data] if ref $data ne 'ARRAY';
return (
$self->_validate_type_array_min_max($_[1], $state),
$self->_validate_type_array_unique($_[1], $state),
$self->_validate_type_array_contains($_[1], $state),
$self->_validate_type_array_items($_[1], $state),
);
}
sub _validate_type_array_contains {
my ($self, $data, $state) = @_;
return unless exists $state->{schema}{contains};
my (@e, @errors);
for my $i (0 .. @$data - 1) {
my @tmp = $self->_validate($data->[$i],
$self->_state($state, path => [@{$state->{path}}, $i], schema => $state->{schema}{contains}));
push @e, \@tmp if @tmp;
}
push @errors, map {@$_} @e if @e >= @$data;
push @errors, E $state->{path}, [array => 'contains'] if not @$data;
return @errors;
}
sub _validate_type_object {
my ($self, $data, $state) = @_;
return E $state->{path}, [object => type => data_type $data] if ref $data ne 'HASH';
return (
$self->_validate_type_object_min_max($_[1], $state),
$self->_validate_type_object_names($_[1], $state),
$self->_validate_type_object_properties($_[1], $state),
$self->_validate_type_object_dependencies($_[1], $state),
);
}
sub _validate_type_object_names {
my ($self, $data, $state) = @_;
return unless exists $state->{schema}{propertyNames};
my @errors;
for my $name (keys %$data) {
next unless my @e = $self->_validate($name, $self->_state($state, schema => $state->{schema}{propertyNames}));
push @errors, prefix_errors propertyName => map [$name, $_], @e;
}
return @errors;
}
*_validate_type_array_items = \&JSON::Validator::Schema::Draft4::_validate_type_array_items;
*_validate_type_array_min_max = \&JSON::Validator::Schema::Draft4::_validate_type_array_min_max;
*_validate_type_array_unique = \&JSON::Validator::Schema::Draft4::_validate_type_array_unique;
*_validate_type_object_dependencies = \&JSON::Validator::Schema::Draft4::_validate_type_object_dependencies;
*_validate_type_object_min_max = \&JSON::Validator::Schema::Draft4::_validate_type_object_min_max;
*_validate_type_object_properties = \&JSON::Validator::Schema::Draft4::_validate_type_object_properties;
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema::Draft6 - JSON-Schema Draft 6
=head1 SYNOPSIS
See L.
=head1 DESCRIPTION
This class represents
L.
=head1 ATTRIBUTES
=head2 specification
my $str = $schema->specification;
Defaults to "L".
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Formats.pm 0000644 0000765 0000024 00000030251 15211411456 021250 0 ustar jhthorsen staff package JSON::Validator::Formats;
use Mojo::Base -strict;
use Scalar::Util 'looks_like_number';
require Time::Local;
use constant DATA_VALIDATE_DOMAIN => eval 'require Data::Validate::Domain;1';
use constant DATA_VALIDATE_IP => eval 'require Data::Validate::IP;1';
use constant IV_SIZE => eval 'require Config;$Config::Config{ivsize}';
use constant NET_IDN_ENCODE => eval 'require Net::IDN::Encode;1';
use constant WARN_MISSING_MODULE => $ENV{JSON_VALIDATOR_WARN} // 1;
our $IRI_TEST_NAME = 'iri-reference';
sub check_byte {
$_[0] =~ /^[A-Za-z0-9\+\/\=]+$/ ? undef : 'Does not match byte format.';
}
sub check_date {
my @date = $_[0] =~ m!^(\d{4})-(\d\d)-(\d\d)$!io;
return 'Does not match date format.' unless @date;
@date = map { s/^0+//; $_ || 0 } reverse @date;
$date[1] -= 1; # month are zero based
local $@;
return undef if eval { Time::Local::timegm(0, 0, 0, @date); 1 };
my $err = (split / at /, $@)[0];
$err =~ s!('-?\d+'\s|\s[\d\.]+)!!g;
$err .= '.';
return $err;
}
sub check_date_time {
my @dt = $_[0] =~ m!^(\d{4})-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d(?:\.\d+)?)(?:Z|([+-])(\d\d):(\d\d))?$!io;
return 'Does not match date-time format.' unless @dt;
return 'Time offset hour out of range.' if defined $dt[7] and $dt[7] > 23;
return 'Time offset minute out of range.' if defined $dt[8] and $dt[8] > 59;
@dt = map { s/^0//; $_ } reverse @dt[0 .. 5];
$dt[4] -= 1; # month are zero based
local $@;
return undef if eval { Time::Local::timegm(@dt); 1 };
my $err = (split / at /, $@)[0];
$err =~ s!('-?\d+'\s|\s[\d\.]+)!!g;
$err .= '.';
return $err;
}
sub check_double { _match_number(double => $_[0], '') }
sub check_duration {
state $rfc3339_duration_re = do {
my $num = qr{\d+(?:[,.]\d+)?};
my $sec = qr/${num}S/;
my $min = qr/${num}M(?:$sec)?/;
my $hour = qr/${num}H(?:$min)?/;
my $day = qr/${num}D(?:$hour)?/;
my $mon = qr/${num}M(?:$day)?/;
my $year = qr/${num}Y(?:$mon)?/;
my $week = qr/${num}W/;
my $time = qr/T($hour|$min|$sec)/;
my $date = qr/(?:$day|$mon|$year)(?:$time)?/;
qr{^P(?:$date|$time|$week)$};
};
return $_[0] =~ $rfc3339_duration_re ? undef : 'Does not match duration format.';
}
sub check_email {
state $email_rfc5322_re = do {
my $atom = qr;[a-zA-Z0-9_!#\$\%&'*+/=?\^`{}~|\-]+;o;
my $quoted_string = qr/"(?:\\[^\r\n]|[^\\"])*"/o;
my $domain_literal = qr/\[(?:\\[\x01-\x09\x0B-\x0c\x0e-\x7f]|[\x21-\x5a\x5e-\x7e])*\]/o;
my $dot_atom = qr/$atom(?:[.]$atom)*/o;
my $local_part = qr/(?:$dot_atom|$quoted_string)/o;
my $domain = qr/(?:$dot_atom|$domain_literal)/o;
qr/^$local_part\@$domain$/o;
};
return $_[0] =~ $email_rfc5322_re ? undef : 'Does not match email format.';
}
sub check_float { _match_number(float => $_[0], '') }
sub check_hostname {
return _module_missing(hostname => 'Data::Validate::Domain') unless DATA_VALIDATE_DOMAIN;
return undef if Data::Validate::Domain::is_hostname($_[0]);
return 'Does not match hostname format.';
}
sub check_idn_email {
return _module_missing('idn-email' => 'Net::IDN::Encode') unless NET_IDN_ENCODE;
local $@;
my $err = eval {
my ($local_part, $domain) = split /@/, $_[0], 2;
$local_part = join '.', map {
Net::IDN::Encode::to_ascii($_)
} split( /\./, $local_part // '');
check_email(
join '@',
$local_part,
Net::IDN::Encode::domain_to_ascii($domain // ''),
);
};
return $err || $@ ? 'Does not match idn-email format.' : undef;
}
sub check_idn_hostname {
return _module_missing('idn-hostname' => 'Net::IDN::Encode') unless NET_IDN_ENCODE;
local $@;
my $err = eval { check_hostname(Net::IDN::Encode::domain_to_ascii($_[0])) };
return $err ? 'Does not match idn-hostname format.' : $@ || undef;
}
sub check_int32 { _match_number(int32 => $_[0], 'l') }
sub check_int64 { _match_number(int64 => $_[0], IV_SIZE >= 8 ? 'q' : '') }
sub check_iri {
local $IRI_TEST_NAME = 'iri';
return 'Scheme missing.' unless $_[0] =~ m!^\w+:!;
return check_iri_reference($_[0]);
}
sub check_iri_reference {
return "Does not match $IRI_TEST_NAME format."
unless $_[0] =~ m!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!;
my ($scheme, $auth_host, $path, $query, $has_fragment, $fragment) = map { $_ // '' } ($2, $4, $5, $7, $8, $9);
return 'Scheme missing.' if length $auth_host and !length $scheme;
return 'Scheme, path or fragment are required.' unless length($scheme) + length($path) + length($has_fragment);
return 'Scheme must begin with a letter.' if length $scheme and lc($scheme) !~ m!^[a-z][a-z0-9\+\-\.]*$!;
return 'Invalid hex escape.' if $_[0] =~ /%[^0-9a-f]/i;
return 'Hex escapes are not complete.' if $_[0] =~ /%[0-9a-f](:?[^0-9a-f]|$)/i;
if (defined $auth_host and length $auth_host) {
return 'Path cannot be empty and must begin with a /' unless !length $path or $path =~ m!^/!;
}
elsif ($path =~ m!^//!) {
return 'Path cannot not start with //.';
}
return undef;
}
sub check_json_pointer {
return !length $_[0] || $_[0] =~ m!^/! ? undef : 'Does not match json-pointer format.';
}
sub check_ipv4 {
return undef if DATA_VALIDATE_IP and Data::Validate::IP::is_ipv4($_[0]);
my (@octets) = $_[0] =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
return undef if 4 == grep { $_ >= 0 && $_ <= 255 && $_ !~ /^0\d{1,2}$/ } @octets;
return 'Does not match ipv4 format.';
}
sub check_ipv6 {
return _module_missing(ipv6 => 'Data::Validate::IP') unless DATA_VALIDATE_IP;
return undef if Data::Validate::IP::is_ipv6($_[0]);
return 'Does not match ipv6 format.';
}
sub check_relative_json_pointer {
return 'Relative JSON Pointer must start with a non-negative-integer.' unless $_[0] =~ m!^\d+!;
return undef if $_[0] =~ m!^(\d+)#?$!;
return 'Relative JSON Pointer must have "#" or a JSON Pointer.' unless $_[0] =~ m!^\d+(.+)!;
return 'Does not match relative-json-pointer format.' if check_json_pointer($1);
return undef;
}
sub check_regex {
eval {qr{$_[0]}} ? undef : 'Does not match regex format.';
}
sub check_time {
my @time = $_[0] =~ m!^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(?:Z|([+-])(\d+):(\d+))?$!io;
return 'Does not match time format.' unless @time;
@time = map { s/^0//; $_ } reverse @time[0 .. 2];
local $@;
return undef if eval { Time::Local::timegm(@time, 31, 11, 1947); 1 };
my $err = (split / at /, $@)[0];
$err =~ s!('-?\d+'\s|\s[\d\.]+)!!g;
$err .= '.';
return $err;
}
sub check_uri {
return 'An URI can only only contain ASCII characters.' if $_[0] =~ m!\P{ASCII}!;
local $IRI_TEST_NAME = 'uri';
return check_iri_reference($_[0]);
}
sub check_uri_reference {
local $IRI_TEST_NAME = 'uri-reference';
return check_iri_reference($_[0]);
}
sub check_uri_template {
return check_iri($_[0]);
}
sub check_uuid {
state $uuid_re = do {
my $x = qr{[0-9A-Fa-f]};
qr{^$x$x$x$x$x$x$x$x-$x$x$x$x-[0-9]$x$x$x-$x$x$x$x-$x$x$x$x$x$x$x$x$x$x$x$x$};
};
return $_[0] =~ $uuid_re ? undef : 'Does not match uuid format.';
}
sub _match_number {
my ($name, $val, $format) = @_;
return 'Does not look like an integer' if $name =~ m!^int! and $val !~ /^-?\d+(\.\d+)?$/;
return 'Does not look like a number.' unless looks_like_number $val;
return undef unless $format;
return undef if $val eq unpack $format, pack $format, $val;
return "Does not match $name format.";
}
sub _module_missing {
warn "[JSON::Validator] Cannot validate $_[0] format: $_[1] is missing" if WARN_MISSING_MODULE;
return undef;
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::Formats - Functions for validating JSON schema formats
=head1 SYNOPSIS
use JSON::Validator::Formats;
my $error = JSON::Validator::Formats::check_uri($str);
die $error if $error;
my $jv = JSON::Validator->new;
$jv->formats({
"date-time" => JSON::Validator::Formats->can("check_date_time"),
"email" => JSON::Validator::Formats->can("check_email"),
"hostname" => JSON::Validator::Formats->can("check_hostname"),
"ipv4" => JSON::Validator::Formats->can("check_ipv4"),
"ipv6" => JSON::Validator::Formats->can("check_ipv6"),
"regex" => JSON::Validator::Formats->can("check_regex"),
"uri" => JSON::Validator::Formats->can("check_uri"),
"uri-reference" => JSON::Validator::Formats->can("check_uri_reference"),
});
=head1 DESCRIPTION
L is a module with utility functions used by
L to match JSON Schema formats.
All functions return C for success or an error message for failure.
=head1 FUNCTIONS
=head2 check_byte
my $str_or_undef = check_byte $str;
Checks that the string matches byte format.
=head2 check_date
my $str_or_undef = check_date $str;
Validates the date part of a RFC3339 string.
=head2 check_date_time
my $str_or_undef = check_date_time $str;
Validated against RFC3339 timestamp in UTC time. This is formatted as
"YYYY-MM-DDThh:mm:ss.fffZ". The milliseconds portion (".fff") is optional
=head2 check_duration
my $str_or_undef = check_duration $str;
Validate a RFC3339 duration string, such as "P3Y6M4DT12H30M5S".
=head2 check_double
my $str_or_undef = check_double $number;
Tries to check if the number is a double. Note that this check is not very
accurate.
=head2 check_email
my $str_or_undef = check_email $str;
Validated against the RFC5322 spec.
=head2 check_float
my $str_or_undef = check_float $number;
Tries to check if the number is a float. Note that this check is not very
accurate.
=head2 check_hostname
my $str_or_undef = check_hostname $str;
Will be validated using L, if installed.
=head2 check_idn_email
my $str_or_undef = check_idn_email $str;
Will validate an email with non-ASCII characters using L if
installed.
=head2 check_idn_hostname
my $str_or_undef = check_idn_hostname $str;
Will validate a hostname with non-ASCII characters using L if
installed.
=head2 check_int32
my $str_or_undef = check_int32 $number;
Tries to check if the number is a int32. Note that this check is not very
accurate.
=head2 check_int64
my $str_or_undef = check_int64 $number;
Tries to check if the number is a int64. Note that this check is not very
accurate.
=head2 check_ipv4
my $str_or_undef = check_ipv4 $str;
Will be validated using L, if installed or fall
back to a plain IPv4 IP regex.
=head2 check_ipv6
my $str_or_undef = check_ipv6 $str;
Will be validated using L, if installed.
=head2 check_iri
my $str_or_undef = check_iri $str;
Validate either an absolute IRI containing ASCII or non-ASCII characters,
against the RFC3986 spec.
=head2 check_iri_reference
my $str_or_undef = check_iri_reference $str;
Validate either a relative or absolute IRI containing ASCII or non-ASCII
characters, against the RFC3986 spec.
=head2 check_json_pointer
my $str_or_undef = check_json_pointer $str;
Validates a JSON pointer, such as "/foo/bar/42".
=head2 check_regex
my $str_or_undef = check_regex $str;
Will check if the string is a regex, using C.
=head2 check_relative_json_pointer
my $str_or_undef = check_relative_json_pointer $str;
Validates a relative JSON pointer, such as "0/foo" or "3#".
=head2 check_time
my $str_or_undef = check_time $str;
Validates the time and optionally the offset part of a RFC3339 string.
=head2 check_uri
my $str_or_undef = check_uri $str;
Validate either a relative or absolute URI containing just ASCII characters,
against the RFC3986 spec.
Note that this might change in the future to only check absolute URI.
=head2 check_uri_reference
my $str_or_undef = check_uri_reference $str;
Validate either a relative or absolute URI containing just ASCII characters,
against the RFC3986 spec.
=head2 check_uri_template
my $str_or_undef = check_uri_reference $str;
Validate an absolute URI with template characters.
=head2 check_uuid
my $str_or_undef = check_uuid $str;
Will check if C<$str> looks like an UUID. Example UUID:
"5782165B-6BB6-472F-B3DD-369D707D6C72".
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/URI.pm 0000644 0000765 0000024 00000010673 15211411456 020302 0 ustar jhthorsen staff package JSON::Validator::URI;
use Mojo::Base 'Mojo::URL';
use Exporter qw(import);
use Digest::SHA ();
use Mojo::JSON;
use Scalar::Util qw(blessed);
use constant UUID_NAMESPACE => do {
my $uuid = '1bab225d-1ca6-4cc5-9c53-a37cc7527848'; # UUIDv4
$uuid =~ tr/-//d;
pack 'H*', $uuid;
};
our @EXPORT_OK = qw(uri);
has nid => undef;
has nss => undef;
sub from_data {
my $self = shift->scheme('urn')->nid('uuid');
state $d = Digest::SHA->new(1);
$d->reset->add(UUID_NAMESPACE)->add(Mojo::JSON::encode_json(shift));
my $uuid = substr $d->digest, 0, 16;
substr $uuid, 6, 1, chr(ord(substr $uuid, 6, 1) & 0x0f | 0x50); # set version 5
substr $uuid, 8, 1, chr(ord(substr $uuid, 8, 1) & 0x3f | 0x80); # set variant 2
return $self->nss(sprintf '%s-%s-%s-%s-%s', map { unpack 'H*', $_ } map { substr $uuid, 0, $_, '' } 4, 2, 2, 2, 6);
}
sub parse {
my ($self, $url) = @_;
# URL
return $self->SUPER::parse($url) unless $url =~ m!^urn:(.*)$!i;
# URN
$self->scheme('urn');
# TODO This regex is not 100% correct according to the 1997 changes regarding "?"
return $self unless $1 =~ m/^([a-z0-9][a-z0-9-]{0,31}):([^#]+)(#(.*))?/;
$self->fragment($4) if defined $3;
return $self->nid($1)->nss($2);
}
sub to_abs {
my $self = shift;
my $abs = $self->clone;
return $abs if $abs->is_abs;
# Scheme
my $base = shift || $abs->base;
$abs->base($base)->scheme($base->scheme);
my $scheme = lc($base->scheme // $abs->scheme // '');
if ($scheme eq 'urn') {
return $abs->scheme('urn') if $abs->nid and $abs->nss;
$abs->nid($base->nid)->nss($base->nss);
}
else {
return $abs if $abs->host;
$abs->host($base->host)->port($base->port);
}
$abs->fragment($base->fragment) unless $abs->fragment;
$abs->userinfo($base->userinfo) unless $abs->userinfo;
# Absolute path
my $path = $abs->path;
return $abs if $path->leading_slash;
# Inherit path
if (!@{$path->parts}) {
$abs->path($base->path->clone->canonicalize);
# Query
$abs->query($base->query->clone) unless length $abs->query->to_string;
}
# Merge paths
else { $abs->path($base->path->clone->merge($path)->canonicalize) }
return $abs;
}
sub to_string {
my $self = shift;
# URL
return $self->SUPER::to_string unless 'urn' eq ($self->scheme // '');
# URN
my $urn = sprintf 'urn:%s:%s', $self->nid, $self->nss;
my $p = $self->path_query;
$urn .= $p =~ m!^/! ? $p : "/$p" if length $p;
$urn .= "#$p" if defined($p = $self->fragment);
return $urn;
}
sub to_unsafe_string {
my $self = shift;
return 'urn' eq ($self->scheme // '') ? $self->to_string : $self->SUPER::to_unsafe_string;
}
sub uri {
my ($uri, $base) = @_;
return __PACKAGE__->new unless @_;
$uri = __PACKAGE__->new($uri) unless blessed $uri;
$base = __PACKAGE__->new($base) if $base and !blessed $base;
return $base ? $uri->to_abs($base) : $uri->clone;
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::URI - Uniform Resource Identifier
=head1 SYNOPSIS
use JSON::Validator::URI;
my $urn = JSON::Validator::URI->new('urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f');
my $url = JSON::Validator::URI->new('/foo');
my $url = JSON::Validator::URI->new('https://mojolicious.org');
=head1 DESCRIPTION
L is a class for presenting both URL and URN.
This class is currently EXPERIMENTAL.
=head1 EXPORTED FUNCTIONS
=head2 uri
$uri = uri;
$uri = uri $orig, $base;
Returns a new L object from C<$orig> and C<$base>. Both
variables can be either a string or a L object.
=head1 ATTRIBUTES
L inherits all attributes from L and
implements the following ones.
=head2 nid
$str = $uri->nid;
Returns the NID part of a URN. Example "uuid" or "iban".
=head2 nss
$str = $uri->nss;
Returns the NSS part of a URN. Example "6e8bc430-9c3a-11d9-9669-0800200c9a66".
=head1 METHODS
L inherits all methods from L and implements
the following ones.
=head2 from_data
$str = $uri->from_data($data);
This method will generate a URN for C<$data>. C<$data> will be serialized
using L before being used to generate an UUIDv5.
This method is EXPERIMENTAL and subject to change!
=head2 parse
See L.
=head2 to_abs
See L.
=head2 to_string
See L.
=head2 to_unsafe_string
See L.
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/lib/JSON/Validator/Schema.pm 0000644 0000765 0000024 00000112451 15211411456 021040 0 ustar jhthorsen staff package JSON::Validator::Schema;
use Mojo::Base 'JSON::Validator'; # TODO: Change this to "use Mojo::Base -base"
use Carp qw(carp);
use JSON::Validator::Formats;
use JSON::Validator::URI qw(uri);
use JSON::Validator::Util qw(E data_checksum data_type is_bool is_num is_type prefix_errors schema_type str2data);
use List::Util qw(first uniq);
use Mojo::JSON qw(false true);
use Mojo::JSON::Pointer;
use Mojo::Util qw(deprecated);
use Scalar::Util qw(blessed);
has errors => sub {
my $self = shift;
my $specification = $self->specification || 'http://json-schema.org/draft-04/schema#';
my $validator = $self->new(store => $self->store, _refs => {})->coerce('numbers,strings')->resolve($specification);
return [$self->_validate_id($self->id), $validator->validate($self->data)];
};
has formats => sub { shift->_build_formats };
has id => sub {
my $data = shift->data;
return is_type($data, 'HASH') ? $data->{'$id'} || $data->{id} || '' : '';
};
has moniker => sub {
my $self = shift;
return "draft$1" if $self->specification =~ m!draft-(\d+)!;
return '';
};
has recursive_data_protection => 1;
has specification => sub {
my $data = shift->data;
is_type($data, 'HASH') ? $data->{'$schema'} || $data->{schema} || '' : '';
};
has _ref_keys => sub { [qw($ref)] };
has _refs => sub { +{} };
sub bundle {
my ($self, $args) = @_;
my $data = $args->{schema} // $self->data;
# Avoid $ref on top level
while (ref $data eq 'HASH' and $data->{'$ref'} and !ref $data->{'$ref'}) {
$data = $self->_refs->{$data}{schema};
}
# Check if we have a complex schema or not
return $self->new(%$self) unless ref $data eq 'HASH';
my $bundle = $self->new(%$self, data => {}, _refs => {});
local $bundle->{seen_bundle_ref} = {};
local $bundle->{seen_schema} = {};
$bundle->_bundle_from({base_url => $self->id, root => $self->data, schema => $data});
my $id = JSON::Validator::URI->new->from_data($bundle->data);
$bundle->id($bundle->store->exists($id) ? $id : $self->store->add($id, $bundle->data));
return $bundle;
}
sub contains {
deprecated 'contains() is deprecated';
state $p = Mojo::JSON::Pointer->new;
return $p->data(shift->{data})->contains(@_);
}
sub data {
my $self = shift;
return $self->{data} //= {} unless @_;
$self->{data} = shift;
delete $self->{errors};
return $self;
}
sub get {
my ($self, $pointer, $cb) = @_;
my %state = (path => [], root => $self->data, schema => $self->data);
return $self->_get([@$pointer], \%state, $cb) if is_type $pointer, 'ARRAY';
return $self->_get([split '/', $pointer], \%state, $cb) if $pointer =~ s!^/!!;
return length $pointer ? undef : $self->data;
}
sub is_invalid { !!@{shift->errors} }
sub load_and_validate_schema { Carp::confess('load_and_validate_schema(...) is unsupported.') }
sub new {
my $class = shift;
return $class->SUPER::new(@_) unless @_ % 2;
my $data = shift;
$data = str2data $data if !ref $data and $data =~ m!^\{|^---|\n!s;
return $class->SUPER::new(@_)->resolve($data);
}
sub resolve {
my $self = shift;
my $data = shift // $self->data; # $self->data will get deprecated
my $state
= !ref $data ? $self->store->resolve($data)
: (blessed $data && $data->can('to_abs')) ? $self->store->resolve($data->to_abs)
: {root => $data, schema => $data};
$self->_refs({});
$self->data($state->{schema});
$self->id($state->{id} || JSON::Validator::URI->new->from_data($state->{schema})) unless $self->id;
$state->{id} ||= $self->store->exists($self->id) || $self->store->add($self->id, $self->data);
$state->{base_url} ||= $self->id;
my (@topics, @refs, %seen) = ([$state, $state->{schema}]);
# Search the whole document for id/$id/$ref/$recursiveRef/...
TOPIC:
while (@topics) {
my ($state, $schema) = @{shift @topics};
if (is_type $schema, 'ARRAY') {
push @topics, map { [$state, $_] } @$schema;
}
elsif (is_type $schema, 'HASH') {
next TOPIC if $seen{$schema}++;
$state = $self->_resolve_object($state, $schema, \@refs, \my %found);
ref $schema->{$_} and !$found{$_} and push @topics, [$state, $schema->{$_}] for keys %$schema;
}
}
# Need to resolve the $ref/$recursiveRef/... after id/$id/$anchor/... is found above
@topics = ();
while (my $r = shift @refs) {
my ($schema, $state) = @$r;
my $resolved = $self->store->resolve($self->_extract_ref_from_schema($schema), $state);
$self->_refs->{$schema} = $resolved;
push @topics, [$resolved, $resolved->{schema}];
}
# Traverse the newly discovered sub documents, if any
goto TOPIC if @topics;
return $self;
}
sub validate {
my ($self, $data, $schema) = @_;
local $self->{seen} = {};
my %state = (base_url => $self->id, path => [], root => $self->data);
my @errors
= sort { $a->path cmp $b->path } $self->_validate($_[1], $self->_state(\%state, schema => $schema // $self->data));
return @errors;
}
sub schema {
deprecated 'schema() is deprecated';
return $_[0]->can('data') ? $_[0] : $_[0]->SUPER::schema;
}
sub _build_formats {
return {
'byte' => JSON::Validator::Formats->can('check_byte'),
'date' => JSON::Validator::Formats->can('check_date'),
'date-time' => JSON::Validator::Formats->can('check_date_time'),
'duration' => JSON::Validator::Formats->can('check_duration'),
'double' => JSON::Validator::Formats->can('check_double'),
'email' => JSON::Validator::Formats->can('check_email'),
'float' => JSON::Validator::Formats->can('check_float'),
'hostname' => JSON::Validator::Formats->can('check_hostname'),
'idn-email' => JSON::Validator::Formats->can('check_idn_email'),
'idn-hostname' => JSON::Validator::Formats->can('check_idn_hostname'),
'int32' => JSON::Validator::Formats->can('check_int32'),
'int64' => JSON::Validator::Formats->can('check_int64'),
'ipv4' => JSON::Validator::Formats->can('check_ipv4'),
'ipv6' => JSON::Validator::Formats->can('check_ipv6'),
'iri' => JSON::Validator::Formats->can('check_iri'),
'iri-reference' => JSON::Validator::Formats->can('check_iri_reference'),
'json-pointer' => JSON::Validator::Formats->can('check_json_pointer'),
'regex' => JSON::Validator::Formats->can('check_regex'),
'relative-json-pointer' => JSON::Validator::Formats->can('check_relative_json_pointer'),
'string' => sub { undef },
'time' => JSON::Validator::Formats->can('check_time'),
'uri' => JSON::Validator::Formats->can('check_uri'),
'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
'uri-reference' => JSON::Validator::Formats->can('check_uri_reference'),
'uri-template' => JSON::Validator::Formats->can('check_uri_template'),
'uuid' => JSON::Validator::Formats->can('check_uuid'),
};
}
# $self inside _bundle() is the target bundle schema object and not the source
sub _bundle_from {
my ($self, $root_state) = @_;
my @topics = ([$root_state, $root_state->{schema}, $self->data, []]);
while (my $topic = shift @topics) {
my ($state, $source, $target, $path) = @$topic;
next if $state->{seen_schema}{$target}++; # Avoid recursion
if (ref $source eq 'HASH') {
for my $k (keys %$source) {
if ($k eq '$ref') {
local @$state{qw(root_state schema_path)} = ($root_state, $path);
push @topics, $self->_bundle_ref($state, $source, $target);
}
else {
my $type = ref $source->{$k};
$target->{$k} //= $type eq 'HASH' ? {} : $type eq 'ARRAY' ? [] : $source->{$k};
push @topics, [$state, $source->{$k}, $target->{$k}, [@$path, $k]] if $type eq 'HASH' or $type eq 'ARRAY';
}
}
}
elsif (ref $source eq 'ARRAY') {
for my $i (0 .. @$source - 1) {
my $type = ref $source->[$i];
$target->[$i] //= $type eq 'HASH' ? {} : $type eq 'ARRAY' ? [] : $source->[$i];
push @topics, [$state, $source->[$i], $target->[$i], [@$path, $i]] if $type eq 'HASH' or $type eq 'ARRAY';
}
}
else {
warn "This should not happen! _bundle_from << $source => $target";
}
}
}
sub _bundle_ref {
my ($self, $state, $source, $target) = @_;
my $uri
= $state->{base_url} eq $state->{root_state}{base_url}
? uri($source->{'$ref'})
: uri($source->{'$ref'}, $state->{base_url});
my $source_state = $self->store->resolve($source->{'$ref'}, $state);
my $prefix = join '-', map { s!^/+!!; $_ } grep { length $_ } pop @{$uri->path}, $uri->fragment;
my ($i, $suffix, @path) = (-1);
while (++$i <= 3) {
$suffix ||= data_checksum $source_state->{schema} if $i;
@path = $self->_bundle_ref_path_expand($source_state, $i ? join '-', $prefix, substr $suffix, 0, $i * 4 : $prefix);
$path[-1] =~ s!^\W+!!;
$path[-1] =~ s![^\w-]!_!g; # Make a pretty path
last unless $self->{seen_bundle_ref}{@path};
}
my $def_target = $self->data;
$target->{'$ref'} = join '/', '#', @path;
$def_target = $def_target->{$_} //= {} for @path;
$self->_refs->{$target} = $source_state;
return [$source_state, $source_state->{schema}, $def_target, $state->{schema_path}];
}
sub _bundle_ref_path_expand { local $_ = $_[2]; s!^definitions/!!; return 'definitions', $_; }
sub _extract_ref_from_schema { $_[1]->{'$ref'} }
sub _get {
my ($self, $pointer, $state, $cb) = @_;
my $path = $state->{path};
my $schema = $state->{schema};
my $follow_ref = sub {
return if $pointer->[0] and $pointer->[0] eq '$ref';
my $ref_keys = $self->_ref_keys; # ($ref, $recursiveRef ...)
my $schema_lookup = $schema;
while (ref $schema eq 'HASH') {
last unless my $ref_key = first { $schema->{$_} && !ref $schema->{$_} } @$ref_keys;
$state = $self->_refs->{$schema_lookup}
// Carp::confess(qq(resolve() must be called before validate() to lookup "$schema_lookup->{$ref_key}".));
if (is_type $state->{schema}, 'HASH') {
$schema_lookup = $state->{schema};
$schema = {%{$state->{schema}}, %$schema};
$state->{schema}{'$ref'} ? ($schema->{'$ref'} = $state->{schema}{'$ref'}) : delete $schema->{'$ref'};
$state->{schema}{'$recursiveRef'}
? ($schema->{'$recursiveRef'} = $state->{schema}{'$recursiveRef'})
: delete $schema->{'$recursiveRef'};
}
else {
$schema = $schema_lookup = $state->{schema};
}
$state = {%$state, path => $path, schema => $schema};
}
};
$follow_ref->();
while (@$pointer) {
my $p = shift @$pointer;
unless (defined $p) {
my $i = 0;
return Mojo::Collection->new(
map { $self->_get([@$pointer], {%$state, path => [@$path, $_->[1]], schema => $_->[0]}, $cb) }
ref $schema eq 'ARRAY' ? (map { [$_, $i++] } @$schema)
: ref $schema eq 'HASH' ? (map { [$schema->{$_}, $_] } sort keys %$schema)
: ([$schema, '']));
}
$p =~ s!~1!/!g;
$p =~ s/~0/~/g;
push @$path, $p;
if (ref $schema eq 'HASH') {
return undef unless exists $schema->{$p};
$schema = $schema->{$p};
}
elsif (ref $schema eq 'ARRAY') {
return undef unless $p =~ /^\d+$/ and @$schema > $p;
$schema = $schema->[$p];
}
else {
return undef;
}
$follow_ref->();
}
return $cb->($schema, E($path)->path) if $cb;
return $schema;
}
sub _register_ref {
my ($self, $ref, %state) = @_;
$state{base_url} //= $self->id;
$state{id} //= $self->id;
$state{root} //= $self->data;
$state{source} //= '_register_ref';
$self->_refs->{$ref} = \%state;
}
sub _resolve_object {
my ($self, $state, $schema, $refs, $found) = @_;
if ($schema->{id} and !ref $schema->{id}) {
my $id = uri $schema->{id}, $state->{base_url};
$self->store->add($id => $schema);
$state = {%$state, base_url => $id->fragment(undef), id => $id};
}
if ($found->{'$ref'} = $schema->{'$ref'} && !ref $schema->{'$ref'}) {
push @$refs, [$schema, $state];
}
return $state;
}
sub _state {
my ($self, $curr, %override) = @_;
my $schema = $override{schema};
my %seen;
while (ref $schema eq 'HASH' and $schema->{'$ref'} and !ref $schema->{'$ref'}) {
last if $seen{$schema}++;
$schema = $self->_refs->{$schema}{schema}
// Carp::confess(qq(resolve() must be called before validate() to lookup "$schema->{'$ref'}".));
}
return {%$curr, %override, schema => $schema};
}
sub _validate {
my ($self, $data, $state) = @_;
my $schema = $state->{schema};
return $schema ? () : E $state->{path}, [not => 'not'] if is_bool $schema;
my @errors;
if ($self->recursive_data_protection and 2 == grep { ref $_ && !is_bool($_) } $data, $schema) {
my $seen_addr = "$schema:$data";
my $seen = $self->{seen}{$seen_addr};
return @{$seen->[0]} if $seen; # Avoid recursion
# Keep $schema and $data referenced for the rest of this validation run, so
# their memory addresses cannot be reused by transient (merged) schemas built
# later while following $ref/$dynamicRef. Otherwise this address-based cache
# key collides with a freed entry and returns stale errors (GH #272).
$self->{seen}{$seen_addr} = [\@errors, $schema, $data];
}
local $_[1] = $data->TO_JSON if blessed $data and $data->can('TO_JSON');
if ($schema->{not}) {
local $self->{seen} = {};
my @e = $self->_validate($_[1], $self->_state($state, schema => $schema->{not}));
push @errors, E $state->{path}, [not => 'not'] unless @e;
}
if (my $rules = $schema->{allOf}) {
push @errors, $self->_validate_all_of($_[1], {%$state, schema => $rules});
}
if (my $rules = $schema->{anyOf}) {
push @errors, $self->_validate_any_of($_[1], {%$state, schema => $rules});
}
if (my $rules = $schema->{oneOf}) {
push @errors, $self->_validate_one_of($_[1], {%$state, schema => $rules});
}
if (exists $schema->{if}) {
local $self->{seen} = {};
my $rules = !$schema->{if}
|| $self->_validate($_[1], $self->_state($state, schema => $schema->{if})) ? $schema->{else} : $schema->{then};
push @errors, $self->_validate($_[1], $self->_state($state, schema => $rules // {}));
}
my $type = $schema->{type} || schema_type $schema, $_[1];
if (ref $type eq 'ARRAY') {
push @errors, $self->_validate_any_of_types($_[1], {%$state, schema => [map { +{%$schema, type => $_} } @$type]});
}
elsif ($type) {
my $method = sprintf '_validate_type_%s', $type;
push @errors, $self->$method($_[1], $state);
}
return @errors if @errors;
if (exists $schema->{const}) {
push @errors, $self->_validate_type_const($_[1], $state);
}
if ($schema->{enum}) {
push @errors, $self->_validate_type_enum($_[1], $state);
}
return @errors;
}
sub _validate_all_of {
my ($self, $data, $state) = @_;
my (@errors, @errors_with_prefix);
my $i = 0;
for my $rule (@{$state->{schema}}) {
local $self->{seen} = {};
next unless my @e = $self->_validate($_[1], $self->_state($state, schema => $rule));
push @errors, @e;
push @errors_with_prefix, [$i, @e];
}
continue {
$i++;
}
return if not @errors;
my $e = E $state->{path};
return prefix_errors(allOf => @errors_with_prefix)
if @errors == 1
or (grep { $_->details->[1] ne 'type' or $_->path ne $e->path } @errors);
# combine all 'type' errors at the base path together
my @details = map $_->details, @errors;
my $want_types = join '/', uniq map $_->[0], @details;
return $e->details([allOf => type => $want_types, $details[-1][2]]);
}
sub _validate_any_of_types {
my ($self, $data, $state) = @_;
my @errors;
for my $rule (@{$state->{schema}}) {
local $self->{seen} = {};
return unless my @e = $self->_validate($_[1], $self->_state($state, schema => $rule));
push @errors, @e;
}
# favor a non-type error from one of the rules
my $e = E $state->{path};
if (my @e = grep { $_->details->[1] ne 'type' or $_->path ne $e->path } @errors) {
return @e;
}
# the type didn't match any of the rules: combine the errors together
my @details = map $_->details, @errors;
my $want_types = join '/', uniq map $_->[0], @details;
return $e->details([$want_types => 'type', $details[-1][2]]);
}
sub _validate_any_of {
my ($self, $data, $state) = @_;
my (@errors, @errors_with_prefix);
my $i = 0;
for my $rule (@{$state->{schema}}) {
local $self->{seen} = {};
return unless my @e = $self->_validate($_[1], $self->_state($state, schema => $rule));
push @errors, @e;
push @errors_with_prefix, [$i, @e];
}
continue {
$i++;
}
my $e = E $state->{path};
return prefix_errors(anyOf => @errors_with_prefix)
if @errors == 1
or (grep { $_->details->[1] ne 'type' or $_->path ne $e->path } @errors);
# combine all 'type' errors at the base path together
my @details = map $_->details, @errors;
my $want_types = join '/', uniq map $_->[0], @details;
return $e->details([anyOf => type => $want_types, $details[-1][2]]);
}
sub _validate_id {
my ($self, $id) = @_;
return unless length $id;
return E '/id', 'Fragment not allowed.' if $id =~ /\#./;
return E '/id', 'Relative URL not allowed.' unless $id =~ /^\w+:/ or -e $id or $id =~ m!^/!;
return;
}
sub _validate_one_of {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my (@errors, @errors_with_prefix);
my ($i, @passed) = (0);
for my $rule (@{$state->{schema}}) {
local $self->{seen} = {};
my @e = $self->_validate($_[1], $self->_state($state, schema => $rule));
push @passed, $i and next unless @e;
push @errors_with_prefix, [$i, @e];
push @errors, @e;
}
continue {
$i++;
}
return if @passed == 1;
return E $path, [oneOf => 'all_rules_match'] unless @errors;
return E $path, [oneOf => 'n_rules_match', join(', ', @passed)] if @passed;
my $e = E $state->{path};
return prefix_errors(oneOf => @errors_with_prefix)
if @errors == 1
or (grep { $_->details->[1] ne 'type' or $_->path ne $e->path } @errors);
# the type didn't match any of the rules: combine the errors together
my @details = map $_->details, @errors;
my $want_types = join '/', uniq map $_->[0], @details;
return $e->details([oneOf => type => $want_types, $details[-1][2]]);
}
sub _validate_number_max {
my ($self, $value, $state, $expected) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my @errors;
my $cmp_with = $schema->{exclusiveMaximum} // '';
if (is_bool $cmp_with) {
push @errors, E $path, [$expected => ex_maximum => $value, $schema->{maximum}] unless $value < $schema->{maximum};
}
elsif (is_num $cmp_with) {
push @errors, E $path, [$expected => ex_maximum => $value, $cmp_with] unless $value < $cmp_with;
}
if (exists $schema->{maximum}) {
my $cmp_with = $schema->{maximum};
push @errors, E $path, [$expected => maximum => $value, $cmp_with] unless $value <= $cmp_with;
}
return @errors;
}
sub _validate_number_min {
my ($self, $value, $state, $expected) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my @errors;
my $cmp_with = $schema->{exclusiveMinimum} // '';
if (is_bool $cmp_with) {
push @errors, E $path, [$expected => ex_minimum => $value, $schema->{minimum}] unless $value > $schema->{minimum};
}
elsif (is_num $cmp_with) {
push @errors, E $path, [$expected => ex_minimum => $value, $cmp_with] unless $value > $cmp_with;
}
if (exists $schema->{minimum}) {
my $cmp_with = $schema->{minimum};
push @errors, E $path, [$expected => minimum => $value, $cmp_with] unless $value >= $cmp_with;
}
return @errors;
}
sub _validate_type_enum {
my ($self, $data, $state) = @_;
my $enum = $state->{schema}{enum};
my $m = data_checksum $data;
for my $i (@$enum) {
return if $m eq data_checksum $i;
}
$enum = join ', ', map { (!defined or ref) ? Mojo::JSON::encode_json($_) : $_ } @$enum;
return E $state->{path}, [enum => enum => $enum];
}
sub _validate_type_const {
my ($self, $data, $state) = @_;
my $const = $state->{schema}{const};
return if data_checksum($data) eq data_checksum($const);
return E $state->{path}, [const => const => Mojo::JSON::encode_json($const)];
}
sub _validate_format {
my ($self, $value, $state) = @_;
my $format = $state->{schema}{format};
my $code = $self->formats->{$format};
return do { warn "Format rule for '$format' is missing"; return } unless $code;
return unless my $err = $code->($value);
return E $state->{path}, [format => $format, $err];
}
sub _validate_type_any { }
sub _validate_type_array {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my @errors;
if (ref $data ne 'ARRAY') {
return E $path, [array => type => data_type $data];
}
if (defined $schema->{minItems} and $schema->{minItems} > @$data) {
push @errors, E $path, [array => minItems => int(@$data), $schema->{minItems}];
}
if (defined $schema->{maxItems} and $schema->{maxItems} < @$data) {
push @errors, E $path, [array => maxItems => int(@$data), $schema->{maxItems}];
}
if ($schema->{uniqueItems}) {
my %uniq;
for (@$data) {
next if !$uniq{data_checksum($_)}++;
push @errors, E $path, [array => 'uniqueItems'];
last;
}
}
if (exists $schema->{contains}) {
my ($contains, @e) = ($self->_state($state, schema => $schema->{contains}));
for my $i (0 .. @$data - 1) {
my @tmp = $self->_validate($data->[$i], {%$contains, path => [@$path, $i]});
push @e, \@tmp if @tmp;
}
push @errors, map {@$_} @e if @e >= @$data;
push @errors, E $path, [array => 'contains'] if not @$data;
}
if (ref $schema->{items} eq 'ARRAY') {
my $additional_items = $schema->{additionalItems} // {};
my @rules = @{$schema->{items}};
if ($additional_items) {
push @rules, $additional_items while @rules < @$data;
}
if (@rules >= @$data) {
for my $i (0 .. @$data - 1) {
push @errors, $self->_validate($data->[$i], $self->_state($state, path => [@$path, $i], schema => $rules[$i]));
}
}
elsif (!$additional_items) {
push @errors, E $path, [array => additionalItems => int(@$data), int(@rules)];
}
}
elsif (exists $schema->{items}) {
my $items = $self->_state($state, schema => $schema->{items});
for my $i (0 .. @$data - 1) {
push @errors, $self->_validate($data->[$i], {%$items, path => [@$path, $i]});
}
}
return @errors;
}
sub _validate_type_boolean {
my ($self, $value, $state) = @_;
# String that looks like a boolean
if (defined $value and $self->{coerce}{booleans}) {
$_[1] = false if $value =~ m!^(0|false|)$!;
$_[1] = true if $value =~ m!^(1|true)$!;
}
return if is_bool $_[1];
return E $state->{path}, [boolean => type => data_type $value];
}
sub _validate_type_integer {
my ($self, $value, $state) = @_;
my @errors = $self->_validate_type_number($_[1], $state, 'integer');
return @errors if @errors;
return if $value =~ /^-?\d+$/;
return E $state->{path}, [integer => type => data_type $value];
}
sub _validate_type_null {
my ($self, $value, $state) = @_;
return unless defined $value;
return E $state->{path}, [null => type => data_type $value];
}
sub _validate_type_number {
my ($self, $value, $state, $expected) = @_;
my @errors;
$expected ||= 'number';
if (!defined $value or ref $value) {
return E $state->{path}, [$expected => type => data_type $value];
}
unless (is_num $value) {
return E $state->{path}, [$expected => type => data_type $value]
if !$self->{coerce}{numbers} or $value !~ /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
$_[1] = 0 + $value; # coerce input value
}
push @errors, $self->_validate_format($value, $state) if $state->{schema}{format};
push @errors, $self->_validate_number_max($value, $state, $expected);
push @errors, $self->_validate_number_min($value, $state, $expected);
my $d = $state->{schema}{multipleOf};
push @errors, E $state->{path}, [$expected => multipleOf => $d] if $d and ($value / $d) =~ /\.[^0]+$/;
return @errors;
}
sub _validate_type_object {
my ($self, $data, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
return E $path, [object => type => data_type $data] unless ref $data eq 'HASH';
my @errors;
my @dkeys = keys %$data;
if (defined $schema->{maxProperties} and $schema->{maxProperties} < @dkeys) {
push @errors, E $path, [object => maxProperties => int(@dkeys), $schema->{maxProperties}];
}
if (defined $schema->{minProperties} and $schema->{minProperties} > @dkeys) {
push @errors, E $path, [object => minProperties => int(@dkeys), $schema->{minProperties}];
}
if (exists $schema->{propertyNames}) {
my $property_names = $self->_state($state, schema => $schema->{propertyNames});
for my $name (keys %$data) {
next unless my @e = $self->_validate($name, $property_names);
push @errors, prefix_errors propertyName => map [$name, $_], @e;
}
}
my %rules;
for my $k (keys %{$schema->{properties} || {}}) {
my $r = $schema->{properties}{$k};
push @{$rules{$k}}, $r;
if ($self->{coerce}{defaults} and ref $r eq 'HASH' and exists $r->{default} and !exists $data->{$k}) {
$data->{$k} = $r->{default};
}
}
for my $p (keys %{$schema->{patternProperties} || {}}) {
my $r = $schema->{patternProperties}{$p};
push @{$rules{$_}}, $r for grep { $_ =~ /$p/ } @dkeys;
}
my $additional = exists $schema->{additionalProperties} ? $schema->{additionalProperties} : {};
if ($additional) {
$additional = {} unless is_type $additional, 'HASH';
$rules{$_} ||= [$additional] for @dkeys;
}
elsif (my @k = grep { !$rules{$_} } @dkeys) {
local $" = ', ';
return E $path, [object => additionalProperties => join ', ', sort @k];
}
for my $k (uniq @{$schema->{required} || []}) {
next if exists $data->{$k};
push @errors, E [@$path, $k], [object => 'required'];
delete $rules{$k};
}
my $dependencies = $schema->{dependencies} || {};
for my $k (keys %$dependencies) {
next if not exists $data->{$k};
if (ref $dependencies->{$k} eq 'ARRAY') {
push @errors,
map { E [@$path, $_], [object => dependencies => $k] } grep { !exists $data->{$_} } @{$dependencies->{$k}};
}
elsif (ref $dependencies->{$k} eq 'HASH') {
push @errors, $self->_validate_type_object($data, $self->_state($state, schema => $schema->{dependencies}{$k}));
}
}
for my $k (keys %rules) {
for my $r (@{$rules{$k}}) {
next unless exists $data->{$k};
my $s2 = $self->_state($state, path => [@$path, $k], schema => $r);
my @e = $self->_validate($data->{$k}, $s2);
push @errors, @e;
next if @e or !is_type $r, 'HASH';
push @errors, $self->_validate_type_enum($data->{$k}, $s2) if $r->{enum};
push @errors, $self->_validate_type_const($data->{$k}, $s2) if $r->{const};
}
}
return @errors;
}
sub _validate_type_string {
my ($self, $value, $state) = @_;
my ($path, $schema) = @$state{qw(path schema)};
my @errors;
if (!$schema->{type} and !defined $value) {
return;
}
if (!defined $value or ref $value) {
return E $path, [string => type => data_type $value];
}
if (B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK) and 0 + $value eq $value and $value * 0 == 0) {
return E $path, [string => type => data_type $value] unless $self->{coerce}{strings};
$_[1] = "$value"; # coerce input value
}
if ($schema->{format}) {
push @errors, $self->_validate_format($value, $state);
}
if (defined $schema->{maxLength}) {
if (length($value) > $schema->{maxLength}) {
push @errors, E $path, [string => maxLength => length($value), $schema->{maxLength}];
}
}
if (defined $schema->{minLength}) {
if (length($value) < $schema->{minLength}) {
push @errors, E $path, [string => minLength => length($value), $schema->{minLength}];
}
}
if (defined $schema->{pattern}) {
my $p = $schema->{pattern};
push @errors, E $path, [string => pattern => $p] unless $value =~ /$p/;
}
return @errors;
}
1;
=encoding utf8
=head1 NAME
JSON::Validator::Schema - Base class for JSON::Validator schemas
=head1 SYNOPSIS
=head2 Basics
# Create a new schema from a file on disk
# It is also possible to create the object from JSON::Validator::Schema,
# but you most likely want to use one of the subclasses.
my $schema = JSON::Validator::Schema::Draft7->new('file:///cool/beans.yaml');
# Validate the schema
die $schema->errors->[0] if $schema->is_invalid;
# Validate data
my @errors = $schema->validate({some => 'data'});
die $errors[0] if @errors;
=head2 Shared store
my $store = JSON::Validator::Store->new;
my $schema = JSON::Validator::Schema::Draft7->new(store => $store);
# Will not fetch the file from web, if the $store has already retrieved
# the schema
$schema->resolve('https://api.example.com/cool/beans.json');
=head2 Make a new validation class
package JSON::Validator::Schema::SomeSchema;
use Mojo::Base 'JSON::Validator::Schema';
has specification => 'https://api.example.com/my/spec.json#';
1;
=head1 DESCRIPTION
L is the base class for
L,
L,
L,
L,
L and
L.
Any of the classes above can be used instead of L if you know
which draft/version you are working with up front.
=head2 Validation
L can both validate user input and the schema itself.
=over 2
=item *
A L represents a set of validation rules stored in
L. The rules stored in the L attribute will be used when calling
the L method.
=item *
The input to C could be some data from a web request or some other
user input. C returns a list of L objects,
if the user input (input to C) contains invalid data.
=item *
The L and L attributes has nothing to do with user input,
meaning it is I relevant for L. These two accessors are used to
check if the rules/schema stored in L is correct. The validation is
performed against the L. This is pretty much the same as:
my $jv = JSON::Validator->new;
my $draft7 = $jv->schema('http://json-schema.org/draft-07/schema#')->schema;
my $schema = $jv->schema({name => {type => 'string'}})->schema;
my @errors = $draft7->validate($schema->data);
=back
=head1 ATTRIBUTES
=head2 errors
$array_ref = $schema->errors;
Holds the errors after checking L against L.
C<$array_ref> containing no elements means L is valid. Each element in
the array-ref is a L object.
This attribute is I changed by L. It only reflects if the
C<$schema> is valid.
=head2 formats
$hash_ref = $schema->formats;
$schema = $schema->formats(\%hash);
Holds a hash-ref, where the keys are supported JSON type "formats", and
the associated values hold code blocks which can validate the given format.
A code block should return C on success and an error string on error:
sub { return defined $_[0] && $_[0] eq "42" ? undef : "Not the answer." };
See L for a list of supported formats.
=head2 recursive_data_protection
The value of this attribute will be copied into the created L. See
L for more details.
=head2 id
$str = $schema->id;
$schema = $schema->id($str);
Holds the ID for this schema. Usually extracted from C<"$id"> or C<"id"> in
L.
=head2 moniker
$str = $schema->moniker;
$schema = $schema->moniker("some_name");
Used to get/set the moniker for the given schema. Will be "draft04" if
L points to a JSON Schema draft URL, and fallback to
empty string if unable to guess a moniker name.
This attribute will (probably) detect more monikers from a given
L or C in the future.
=head2 recursive_data_protection
$schema = $schema->recursive_data_protection($bool);
$bool = $schema->recursive_data_protection;
Recursive data protection is active by default, however it can be deactivated
by assigning a false value to the L attribute.
Recursive data protection can have a noticeable impact on memory usage when
validating large data structures. If you are encountering issues with memory
and you can guarantee that you do not have any loops in your data structure
then deactivating the recursive data protection may help.
This attribute is EXPERIMENTAL and may change in a future release.
B
=head2 specification
$str = $schema->specification;
$schema = $schema->specification($str);
The URL to the specification used when checking for L. Usually
extracted from C<"$schema"> or C<"schema"> in L.
=head2 store
$store = $schema->store;
Holds a L object that caches the retrieved schemas.
This object can be shared amongst different schema objects to prevent
a schema from having to be downloaded again.
=head1 METHODS
=head2 bundle
$bundled = $schema->bundle;
C<$bundled> is a new L object where none of the "$ref"
will point to external resources. This can be useful, if you want to have a
bunch of files locally, but hand over a single file to a client.
Mojo::File->new("client.json")
->spurt(Mojo::JSON::to_json($schema->bundle->data));
=head2 coerce
$schema = $schema->coerce('bool,def,num,str');
$schema = $schema->coerce('booleans,defaults,numbers,strings');
$hash_ref = $schema->coerce;
Set the given type to coerce. Before enabling coercion this module is very
strict when it comes to validating types. Example: The string C<"1"> is not the
same as the number C<1>, unless you have "numbers" coercion enabled.
=over 2
=item * booleans
Will convert what could be interpreted as a boolean (that is, an actual
numeric C<1> or C<0>, and the strings "true" and "false") to a
L object. Note that "foo" is not considered a true value and
will fail the validation.
=item * defaults
Will copy the default value defined in the schema, into the input structure,
if the input value is non-existing.
Note that support for "default" is currently EXPERIMENTAL, and enabling this
might be changed in future versions.
=item * numbers
Will convert strings that look like numbers, into true numbers. This works for
both the "integer" and "number" types.
=item * strings
Will convert a number into a string. This works for the "string" type.
=back
=head2 contains
This method will be removed in a future release.
=head2 data
my $hash_ref = $schema->data;
my $schema = $schema->data($bool);
my $schema = $schema->data($hash_ref);
Will set a structure representing the schema. In most cases you want to
use L instead of L.
=head2 get
my $data = $schema->get([@json_pointer]);
my $data = $schema->get($json_pointer);
my $data = $schema->get($json_pointer, sub { my ($data, $json_pointer) = @_; });
This method will extract data from L, using a C<$json_pointer> -
L. It can however be used in a more
complex way by passing in an array-ref: The array-ref can contain C
values, will result in extracting any element on that point, regardless of
value. In that case a L will be returned.
A callback can also be given. This callback will be called each time the
C<$json_pointer> matches some data, and will pass in the C<$json_pointer> at
that place.
In addition if this method "sees" a JSON-Schema C<$ref> on the way, the "$ref"
will be followed into any given sub-schema.
=head2 is_invalid
my $bool = $schema->is_invalid;
Returns true if the schema in L is invalid. Internally this method calls
L which will validate L against L.
=head2 load_and_validate_schema
This method is unsupported. Use L or L instead.
=head2 new
my $schema = JSON::Validator::Schema->new($data);
my $schema = JSON::Validator::Schema->new($data, %attributes);
my $schema = JSON::Validator::Schema->new(%attributes);
Construct a new L object. Passing C<$data> as the
first argument will cause L to be called, meaning the constructor
might throw an exception if the schema could not be successfully resolved.
=head2 resolve
$schema = $schema->resolve($uri);
$schema = $schema->resolve($data);
Used to resolve L<$uri> or L<$data> and store the resolved schema in L.
If C<$data> or C<$uri> contain any "$ref", then these schemas will be
downloaded and resolved as well.
If L does not contain an "id" or "$id", then L will be assigned a
autogenerated "urn". This "urn" might be changed in future releases, but should
always be the same for the same L.
=head2 schema
This method will be removed in a future release.
=head2 validate
@errors = $schema->validate($any);
Will validate C<$any> against the schema defined in L. Each element in
C<@errors> is an L object.
=head1 SEE ALSO
L.
=cut
JSON-Validator-5.18/Makefile.PL 0000644 0000765 0000024 00000006635 15210277065 016023 0 ustar jhthorsen staff use 5.016;
use strict;
use warnings;
use utf8;
use ExtUtils::MakeMaker;
my $GITHUB_URL = 'https://github.com/jhthorsen/json-validator';
my @PREREQ_YAML = !$ENV{JSON_VALIDATOR_PREFER_YAML_PP}
|| eval 'use YAML::XS 0.67;1' ? ('YAML::XS' => '0.67') : ('YAML::PP' => '0.020');
my %WriteMakefileArgs = (
NAME => 'JSON::Validator',
ABSTRACT_FROM => 'lib/JSON/Validator.pm',
AUTHOR => 'Jan Henning Thorsen ',
LICENSE => 'artistic_2',
VERSION_FROM => 'lib/JSON/Validator.pm',
PREREQ_PM => {
'perl' => 'v5.16.0',
'Data::Validate::Domain' => '0.11',
'Data::Validate::IP' => '0.27',
'List::Util' => '1.45',
'Mojolicious' => '9.34',
'Net::IDN::Encode' => '2.500',
@PREREQ_YAML
},
TEST_REQUIRES => {'Test::More' => '1.30', 'Test::Deep' => '0'},
META_MERGE => {
'dynamic_config' => 0,
'meta-spec' => {version => 2},
'no_index' => {directory => [qw(examples t)]},
'prereqs' => {runtime => {requires => {perl => '5.016'}}},
'resources' => {
bugtracker => {web => "$GITHUB_URL/issues"},
homepage => $GITHUB_URL,
license => ['http://www.opensource.org/licenses/artistic-license-2.0'],
repository => {type => 'git', url => "$GITHUB_URL.git", web => $GITHUB_URL},
x_IRC => {url => 'irc://irc.libera.chat/#perl-openapi', web => 'https://web.libera.chat/#perl-openapi'},
},
'x_contributors' => [
'Aleksandr Orlenko ',
'Alexander Hartmaier ',
'Alexander Karelas ',
'Bernhard Graf ',
'Brad Barden ',
'Dagfinn Ilmari Mannsåker ',
'Daniel Böhmer ',
'David Cantrell ',
'Ed J ',
'Ere Maijala ',
'Fabrizio Gennari ',
'Ilya Rassadin ',
'Jan Henning Thorsen ',
'Jason Cooper ',
'Karen Etheridge ',
'Kenichi Ishigaki ',
'Kevin M. Goess ',
'Kirill Matusov ',
'Krasimir Berov ',
'Lari Taskula ',
'Lee Johnson ',
'Martin Renvoize ',
'Mattias Päivärinta ',
'Michael Jemmeson ',
'Michael Schout ',
'Mohammad S Anwar ',
'Nick Morrott ',
'Pierre-Aymeric Masse ',
'Roy Storey ',
'Russell Jenkins ',
'Sebastian Riedel ',
'Stephan Hradek ',
'Tim Stallard ',
'Zoffix Znet ',
],
},
test => {TESTS => (-e 'META.yml' ? 't/*.t' : 't/*.t xt/*.t')},
);
unless (eval { ExtUtils::MakeMaker->VERSION('6.63_03') }) {
my $test_requires = delete $WriteMakefileArgs{TEST_REQUIRES};
@{$WriteMakefileArgs{PREREQ_PM}}{keys %$test_requires} = values %$test_requires;
}
WriteMakefile(%WriteMakefileArgs);
JSON-Validator-5.18/META.json 0000644 0000765 0000024 00000007055 15211411470 015456 0 ustar jhthorsen staff {
"abstract" : "Validate data against a JSON schema",
"author" : [
"Jan Henning Thorsen "
],
"dynamic_config" : 0,
"generated_by" : "ExtUtils::MakeMaker version 7.76, CPAN::Meta::Converter version 2.150010",
"license" : [
"artistic_2"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "JSON-Validator",
"no_index" : {
"directory" : [
"t",
"inc",
"examples",
"t"
]
},
"prereqs" : {
"build" : {
"requires" : {
"ExtUtils::MakeMaker" : "0"
}
},
"configure" : {
"requires" : {
"ExtUtils::MakeMaker" : "0"
}
},
"runtime" : {
"requires" : {
"Data::Validate::Domain" : "0.11",
"Data::Validate::IP" : "0.27",
"List::Util" : "1.45",
"Mojolicious" : "9.34",
"Net::IDN::Encode" : "2.500",
"YAML::XS" : "0.67",
"perl" : "5.016"
}
},
"test" : {
"requires" : {
"Test::Deep" : "0",
"Test::More" : "1.30"
}
}
},
"release_status" : "stable",
"resources" : {
"bugtracker" : {
"web" : "https://github.com/jhthorsen/json-validator/issues"
},
"homepage" : "https://github.com/jhthorsen/json-validator",
"license" : [
"http://www.opensource.org/licenses/artistic-license-2.0"
],
"repository" : {
"type" : "git",
"url" : "https://github.com/jhthorsen/json-validator.git",
"web" : "https://github.com/jhthorsen/json-validator"
},
"x_IRC" : {
"url" : "irc://irc.libera.chat/#perl-openapi",
"web" : "https://web.libera.chat/#perl-openapi"
}
},
"version" : "5.18",
"x_contributors" : [
"Aleksandr Orlenko ",
"Alexander Hartmaier ",
"Alexander Karelas ",
"Bernhard Graf ",
"Brad Barden ",
"Dagfinn Ilmari Mannsåker ",
"Daniel Böhmer ",
"David Cantrell ",
"Ed J ",
"Ere Maijala ",
"Fabrizio Gennari ",
"Ilya Rassadin ",
"Jan Henning Thorsen ",
"Jason Cooper ",
"Karen Etheridge ",
"Kenichi Ishigaki ",
"Kevin M. Goess ",
"Kirill Matusov ",
"Krasimir Berov ",
"Lari Taskula ",
"Lee Johnson ",
"Martin Renvoize ",
"Mattias Päivärinta ",
"Michael Jemmeson ",
"Michael Schout ",
"Mohammad S Anwar ",
"Nick Morrott ",
"Pierre-Aymeric Masse ",
"Roy Storey ",
"Russell Jenkins ",
"Sebastian Riedel ",
"Stephan Hradek ",
"Tim Stallard ",
"Zoffix Znet "
],
"x_serialization_backend" : "JSON::PP version 4.16"
}