File Coverage

blib/lib/JSON/Schema/Modern/Vocabulary/OpenAPI_3_0.pm
Criterion Covered Total %
statement 65 66 98.4
branch 4 6 66.6
condition 12 33 36.3
subroutine 21 22 95.4
pod 0 2 0.0
total 102 129 79.0


line stmt bran cond sub pod time code
1 4     4   3130 use strictures 2;
  4         85  
  4         148  
2             package JSON::Schema::Modern::Vocabulary::OpenAPI_3_0;
3             # vim: set ts=8 sts=2 sw=2 tw=100 et :
4             # ABSTRACT: Implementation of the JSON Schema OpenAPI 3.0 pseudo-vocabulary
5              
6             our $VERSION = '0.139';
7              
8 4     4   1632 use 5.020;
  4         12  
9 4     4   18 use utf8;
  4         20  
  4         50  
10 4     4   99 use Moo;
  4         8  
  4         89  
11 4     4   1385 use strictures 2;
  4         19  
  4         123  
12 4     4   1283 use stable 0.031 'postderef';
  4         63  
  4         27  
13 4     4   863 use experimental 'signatures';
  4         7  
  4         34  
14 4     4   215 no autovivification warn => qw(fetch store exists delete);
  4         7  
  4         48  
15 4     4   296 use if "$]" >= 5.022, experimental => 're_strict';
  4         9  
  4         100  
16 4     4   334 no if "$]" >= 5.031009, feature => 'indirect';
  4         8  
  4         281  
17 4     4   19 no if "$]" >= 5.033001, feature => 'multidimensional';
  4         21  
  4         191  
18 4     4   16 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  4         7  
  4         200  
19 4     4   15 no if "$]" >= 5.041009, feature => 'smartmatch';
  4         17  
  4         154  
20 4     4   16 no feature 'switch';
  4         8  
  4         170  
21 4     4   24 use Scalar::Util 'looks_like_number';
  4         9  
  4         376  
22 4     4   19 use JSON::Schema::Modern::Utilities qw(get_type E);
  4         104  
  4         234  
23 4     4   18 use namespace::clean;
  4         6  
  4         51  
24              
25             with 'JSON::Schema::Modern::Vocabulary';
26              
27       24 0   sub vocabulary {}
28              
29             my $vocabulary_mapping = {
30             Core => [ '$ref' ],
31             Validation => [ qw(
32             type
33             enum
34             multipleOf
35             maximum
36             exclusiveMaximum
37             minimum
38             exclusiveMinimum
39             maxLength
40             minLength
41             pattern
42             maxItems
43             minItems
44             uniqueItems
45             maxProperties
46             minProperties
47             required
48             ) ],
49             Applicator => [ qw(
50             allOf
51             anyOf
52             oneOf
53             not
54             items
55             properties
56             additionalProperties
57             ) ],
58             Format => [ 'format' ],
59             MetaData => [ qw(
60             title
61             description
62             default
63             deprecated
64             readOnly
65             writeOnly
66             example
67             ) ],
68             # unique to OAS; all of these have null implementations
69             Other => [ qw(
70             nullable
71             discriminator
72             externalDocs
73             ) ],
74             };
75              
76             sub keywords {
77             # most of these can be directly mapped to JSON Schema vocabulary keywords (3.0.4 §4.7.24.1).
78 2     2 0 1790 map @$_, $vocabulary_mapping->@{qw(Core Validation Applicator Format MetaData Other)};
79             }
80              
81              
82             # v3.0.4 §4.4: "Data types in the OAS are based on the non-null types supported by the JSON Schema
83             # Validation Specification Draft Wright-00: “boolean”, “object”, “array”, “number”, “string”, or
84             # “integer”. See nullable for an alternative solution to “null” as a type."
85             # v3.0.4 §4.7.24.1: ""items" MUST be present if type is "array"."
86             # "["type"] Value MUST be a string. Multiple types via an array are not supported."
87              
88 11     11   7729 sub _eval_keyword_type ($class, $data, $schema, $state) {
  11         22  
  11         15  
  11         12  
  11         15  
  11         12  
89 11 100 100     45 return 1 if not defined $data and $schema->{nullable};
90              
91 9         78 my $type = get_type($data, { legacy_ints => 1 });
92 9         202 my $want = $schema->{type};
93              
94             return 1 if
95             $type eq $want or ($want eq 'number' and $type eq 'integer')
96             or ($type eq 'string' and $state->{stringy_numbers} and looks_like_number($data)
97             and ($want eq 'number' or ($want eq 'integer' and $data == int($data))))
98 9 0 33     56 or ($want eq 'boolean' and $state->{scalarref_booleans} and $type eq 'reference to SCALAR');
      66        
      33        
      33        
      0        
      0        
      33        
      33        
      33        
      33        
99              
100 3 100       18 return E($state, 'got %s, not %s%s', $type, $want, $schema->{nullable} ? ' or null' : '');
101             }
102              
103             foreach my $vocabulary (keys %$vocabulary_mapping) {
104             foreach my $keyword ($vocabulary_mapping->{$vocabulary}->@*) {
105              
106             # There is no need to rigorously traverse schemas in v3.0 OpenAPI documents, as there are no
107             # embedded identifiers to find ("id" is not a valid keyword).
108             # The document validation pass on the document that we already do is sufficient to check the
109             # schema keywords' syntax, and this saves us from having to implement custom methods for every
110             # keyword that uses a subset of normal draft4 syntax (see v3.0.4 §4.7.24.1).
111              
112             my $k = $keyword =~ s/^\$//r;
113              
114 4     4   3345 no strict 'refs';
  4         10  
  4         834  
115 0     0     *{__PACKAGE__.'::_traverse_keyword_'.$k} = sub { 1 };
116              
117             next if $keyword eq 'type';
118              
119             # these keywords are no-ops
120             next if $vocabulary eq 'MetaData' or $vocabulary eq 'Other';
121              
122             my $name = '_eval_keyword_'.$k;
123             *{__PACKAGE__.'::'.$name} = *{'JSON::Schema::Modern::Vocabulary::'.$vocabulary.'::'.$name};
124             }
125             }
126              
127             1;
128              
129             __END__