File Coverage

blib/lib/JSON/Schema/Modern/Vocabulary/Content.pm
Criterion Covered Total %
statement 104 105 99.0
branch 26 28 92.8
condition 3 3 100.0
subroutine 23 24 95.8
pod 0 3 0.0
total 156 163 95.7


line stmt bran cond sub pod time code
1 31     31   16590 use strict;
  31         121  
  31         1030  
2 31     31   263 use warnings;
  31         124  
  31         1697  
3             package JSON::Schema::Modern::Vocabulary::Content;
4             # vim: set ts=8 sts=2 sw=2 tw=100 et :
5             # ABSTRACT: Implementation of the JSON Schema Content vocabulary
6              
7             our $VERSION = '0.570';
8              
9 31     31   576 use 5.020;
  31         160  
10 31     31   191 use Moo;
  31         121  
  31         254  
11 31     31   11597 use strictures 2;
  31         267  
  31         1239  
12 31     31   5805 use stable 0.031 'postderef';
  31         557  
  31         216  
13 31     31   4430 use experimental 'signatures';
  31         147  
  31         179  
14 31     31   2745 use if "$]" >= 5.022, experimental => 're_strict';
  31         140  
  31         326  
15 31     31   3056 no if "$]" >= 5.031009, feature => 'indirect';
  31         107  
  31         314  
16 31     31   1826 no if "$]" >= 5.033001, feature => 'multidimensional';
  31         125  
  31         261  
17 31     31   1656 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  31         126  
  31         301  
18 31     31   1440 use Storable 'dclone';
  31         90  
  31         2102  
19 31     31   268 use Feature::Compat::Try;
  31         114  
  31         262  
20 31     31   4120 use JSON::Schema::Modern::Utilities qw(is_type A assert_keyword_type E abort);
  31         106  
  31         2518  
21 31     31   257 use namespace::clean;
  31         100  
  31         224  
22              
23             with 'JSON::Schema::Modern::Vocabulary';
24              
25             sub vocabulary {
26 15     15 0 80 'https://json-schema.org/draft/2019-09/vocab/content' => 'draft2019-09',
27             'https://json-schema.org/draft/2020-12/vocab/content' => 'draft2020-12';
28             }
29              
30 0     0 0 0 sub evaluation_order { 4 }
31              
32 89     89 0 217 sub keywords ($self, $spec_version) {
  89         237  
  89         170  
  89         194  
33             return (
34 89 100       1345 qw(contentEncoding contentMediaType),
35             $spec_version ne 'draft7' ? 'contentSchema' : (),
36             );
37             }
38              
39 171     171   297 sub _traverse_keyword_contentEncoding ($self, $schema, $state) {
  171         538  
  171         281  
  171         271  
  171         266  
40 171 50       497 return if not assert_keyword_type($state, $schema, 'string');
41 171         513 return 1;
42             }
43              
44 85     85   164 sub _eval_keyword_contentEncoding ($self, $data, $schema, $state) {
  85         172  
  85         142  
  85         157  
  85         147  
  85         132  
45 85 100       340 return 1 if not is_type('string', $data);
46              
47 69         355 A($state, $schema->{$state->{keyword}});
48              
49 69 100       211 if ($state->{validate_content_schemas}) {
50 19         493 my $decoder = $state->{evaluator}->get_encoding($schema->{contentEncoding});
51             abort($state, 'cannot find decoder for contentEncoding "%s"', $schema->{contentEncoding})
52 19 100       1833 if not $decoder;
53              
54             # decode the data now, so we can report errors for the right keyword
55             try {
56             $state->{_content_ref} = $decoder->(\$data);
57             }
58 18         56 catch ($e) {
59             chomp $e;
60             return E($state, 'could not decode %s string: %s', $schema->{contentEncoding}, $e);
61             };
62             }
63              
64 62         227 return 1;
65             }
66              
67 86     86   337 sub _traverse_keyword_contentMediaType { goto \&_traverse_keyword_contentEncoding }
68              
69 84     84   151 sub _eval_keyword_contentMediaType ($self, $data, $schema, $state) {
  84         169  
  84         183  
  84         140  
  84         144  
  84         171  
70 84 100       254 return 1 if not is_type('string', $data);
71              
72 68         316 A($state, $schema->{$state->{keyword}});
73              
74 68 100       205 if ($state->{validate_content_schemas}) {
75 18         466 my $decoder = $state->{evaluator}->get_media_type($schema->{contentMediaType});
76             abort($state, 'cannot find decoder for contentMediaType "%s"', $schema->{contentMediaType})
77 18 100       155 if not $decoder;
78              
79             # contentEncoding failed to decode the content
80 17 100 100     118 return 1 if exists $schema->{contentEncoding} and not exists $state->{_content_ref};
81              
82             # decode the data now, so we can report errors for the right keyword
83             try {
84             $state->{_content_ref} = $decoder->($state->{_content_ref} // \$data);
85             }
86 14         41 catch ($e) {
87             chomp $e;
88             delete $state->{_content_ref};
89             return E($state, 'could not decode %s string: %s', $schema->{contentMediaType}, $e);
90             }
91             }
92              
93 58         448 return 1;
94             }
95              
96 41     41   72 sub _traverse_keyword_contentSchema ($self, $schema, $state) {
  41         70  
  41         76  
  41         75  
  41         85  
97             # since contentSchema should never be assumed to be evaluated in the context of the containing
98             # schema, it is not appropriate to gather identifiers found therein -- but we can still validate
99             # the subschema.
100 41         508 $self->traverse_subschema($schema, +{ %$state, identifiers => [] });
101             }
102              
103 39     39   72 sub _eval_keyword_contentSchema ($self, $data, $schema, $state) {
  39         72  
  39         65  
  39         71  
  39         68  
  39         72  
104 39 50       97 return 1 if not exists $schema->{contentMediaType};
105 39 100       102 return 1 if not is_type('string', $data);
106              
107 35         2398 A($state, dclone($schema->{contentSchema}));
108 35 100       408 return 1 if not $state->{validate_content_schemas};
109              
110 5 100       22 return 1 if not exists $state->{_content_ref}; # contentMediaType failed to decode the content
111              
112             return 1 if $self->eval($state->{_content_ref}->$*, $schema->{contentSchema},
113 3 100       65 { %$state, schema_path => $state->{schema_path}.'/contentSchema' });
114 2         17 return E($state, 'subschema is not valid');
115             }
116              
117             1;
118              
119             __END__
120              
121             =pod
122              
123             =encoding UTF-8
124              
125             =head1 NAME
126              
127             JSON::Schema::Modern::Vocabulary::Content - Implementation of the JSON Schema Content vocabulary
128              
129             =head1 VERSION
130              
131             version 0.570
132              
133             =head1 DESCRIPTION
134              
135             =for Pod::Coverage vocabulary evaluation_order keywords
136              
137             =for stopwords metaschema
138              
139             Implementation of the JSON Schema Draft 2020-12 "Content" vocabulary, indicated in metaschemas
140             with the URI C<https://json-schema.org/draft/2020-12/vocab/content> and formally specified in
141             L<https://json-schema.org/draft/2020-12/json-schema-validation.html#section-8>.
142              
143             Support is also provided for
144              
145             =over 4
146              
147             =item *
148              
149             the equivalent Draft 2019-09 keywords, indicated in metaschemas with the URI C<https://json-schema.org/draft/2019-09/vocab/content> and formally specified in L<https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-02#section-8>.
150              
151             =item *
152              
153             the equivalent Draft 7 keywords that correspond to this vocabulary and are formally specified in L<https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-8>.
154              
155             =back
156              
157             Assertion behaviour can be enabled by toggling the L<JSON::Schema::Modern/validate_content_schemas>
158             option.
159              
160             New handlers for C<contentEncoding> and C<contentMediaType> can be done through
161             L<JSON::Schema::Modern/add_encoding> and L<JSON::Schema::Modern/add_media_type>.
162              
163             =for stopwords OpenAPI
164              
165             =head1 SUPPORT
166              
167             Bugs may be submitted through L<https://github.com/karenetheridge/JSON-Schema-Modern/issues>.
168              
169             I am also usually active on irc, as 'ether' at C<irc.perl.org> and C<irc.libera.chat>.
170              
171             You can also find me on the L<JSON Schema Slack server|https://json-schema.slack.com> and L<OpenAPI Slack
172             server|https://open-api.slack.com>, which are also great resources for finding help.
173              
174             =head1 AUTHOR
175              
176             Karen Etheridge <ether@cpan.org>
177              
178             =head1 COPYRIGHT AND LICENCE
179              
180             This software is copyright (c) 2020 by Karen Etheridge.
181              
182             This is free software; you can redistribute it and/or modify it under
183             the same terms as the Perl 5 programming language system itself.
184              
185             =cut