line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package JSONSchema::Validator::URIResolver; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: URI resolver |
4
|
|
|
|
|
|
|
|
5
|
6
|
|
|
6
|
|
42
|
use strict; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
198
|
|
6
|
6
|
|
|
6
|
|
31
|
use warnings; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
191
|
|
7
|
6
|
|
|
6
|
|
32
|
use Carp 'croak'; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
363
|
|
8
|
|
|
|
|
|
|
|
9
|
6
|
|
|
6
|
|
34
|
use Scalar::Util 'weaken'; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
200
|
|
10
|
|
|
|
|
|
|
|
11
|
6
|
|
|
6
|
|
30
|
use URI; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
106
|
|
12
|
6
|
|
|
6
|
|
25
|
use URI::Escape; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
247
|
|
13
|
6
|
|
|
6
|
|
2911
|
use Encode; |
|
6
|
|
|
|
|
53915
|
|
|
6
|
|
|
|
|
471
|
|
14
|
|
|
|
|
|
|
|
15
|
6
|
|
|
6
|
|
50
|
use JSONSchema::Validator::JSONPointer 'json_pointer'; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
255
|
|
16
|
6
|
|
|
6
|
|
37
|
use JSONSchema::Validator::Util qw(get_resource decode_content); |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
5642
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# what keys contain the schema? Required to find an $id in a schema |
19
|
|
|
|
|
|
|
my $SEARCH_ID = { |
20
|
|
|
|
|
|
|
value => { |
21
|
|
|
|
|
|
|
additionalItems => 1, |
22
|
|
|
|
|
|
|
items => 1, |
23
|
|
|
|
|
|
|
additionalProperties => 1, |
24
|
|
|
|
|
|
|
not => 1, |
25
|
|
|
|
|
|
|
propertyNames => 1, |
26
|
|
|
|
|
|
|
contains => 1, |
27
|
|
|
|
|
|
|
if => 1, |
28
|
|
|
|
|
|
|
then => 1, |
29
|
|
|
|
|
|
|
else => 1 |
30
|
|
|
|
|
|
|
}, |
31
|
|
|
|
|
|
|
kv_value => { |
32
|
|
|
|
|
|
|
properties => 1, |
33
|
|
|
|
|
|
|
patternProperties => 1, |
34
|
|
|
|
|
|
|
dependencies => 1, |
35
|
|
|
|
|
|
|
definitions => 1 |
36
|
|
|
|
|
|
|
}, |
37
|
|
|
|
|
|
|
arr_value => { |
38
|
|
|
|
|
|
|
items => 1, |
39
|
|
|
|
|
|
|
allOf => 1, |
40
|
|
|
|
|
|
|
anyOf => 1, |
41
|
|
|
|
|
|
|
oneOf => 1 |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
}; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new { |
46
|
321
|
|
|
321
|
0
|
1105
|
my ($class, %params) = @_; |
47
|
|
|
|
|
|
|
|
48
|
321
|
50
|
|
|
|
726
|
croak 'URIResolver: validator must be specified' unless $params{validator}; |
49
|
321
|
50
|
|
|
|
613
|
croak 'URIResolver: schema must be specified' unless defined $params{schema}; |
50
|
|
|
|
|
|
|
|
51
|
321
|
|
|
|
|
499
|
my $validator = $params{validator}; |
52
|
321
|
|
|
|
|
422
|
my $schema = $params{schema}; |
53
|
321
|
|
50
|
|
|
604
|
my $base_uri = $params{base_uri} // ''; |
54
|
|
|
|
|
|
|
|
55
|
321
|
|
50
|
|
|
578
|
my $scheme_handlers = $params{scheme_handlers} // {}; |
56
|
|
|
|
|
|
|
|
57
|
321
|
|
|
|
|
1038
|
weaken($validator); |
58
|
|
|
|
|
|
|
|
59
|
321
|
|
|
|
|
952
|
my $self = { |
60
|
|
|
|
|
|
|
validator => $validator, |
61
|
|
|
|
|
|
|
cache => { |
62
|
|
|
|
|
|
|
$base_uri => $schema |
63
|
|
|
|
|
|
|
}, |
64
|
|
|
|
|
|
|
scheme_handlers => $scheme_handlers |
65
|
|
|
|
|
|
|
}; |
66
|
|
|
|
|
|
|
|
67
|
321
|
|
|
|
|
560
|
bless $self, $class; |
68
|
|
|
|
|
|
|
|
69
|
321
|
100
|
|
|
|
830
|
if ('#' eq substr $base_uri, -1) { |
70
|
21
|
|
|
|
|
63
|
$base_uri = substr $base_uri, 0, - 1; |
71
|
21
|
|
|
|
|
55
|
$self->{cache}{$base_uri} = $schema; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
321
|
100
|
100
|
|
|
742
|
$self->cache_id(URI->new($base_uri), $schema) if $validator->using_id_with_ref && ref $schema eq 'HASH'; |
75
|
|
|
|
|
|
|
|
76
|
321
|
|
|
|
|
1182
|
return $self; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
5243
|
|
|
5243
|
0
|
13155
|
sub validator { shift->{validator} } |
80
|
0
|
|
|
0
|
0
|
0
|
sub scheme_handlers { shift->{scheme_handlers} } |
81
|
1381
|
|
|
1381
|
0
|
5273
|
sub cache { shift->{cache} } |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# self - URIResolver |
84
|
|
|
|
|
|
|
# origin_uri - URI |
85
|
|
|
|
|
|
|
# return (scope|string, schema) |
86
|
|
|
|
|
|
|
sub resolve { |
87
|
454
|
|
|
454
|
0
|
806
|
my ($self, $origin_uri) = @_; |
88
|
|
|
|
|
|
|
|
89
|
454
|
100
|
|
|
|
827
|
return ($origin_uri->as_string, $self->cache->{$origin_uri->as_string}) if exists $self->cache->{$origin_uri->as_string}; |
90
|
|
|
|
|
|
|
|
91
|
142
|
|
|
|
|
906
|
my $uri = $origin_uri->clone; |
92
|
142
|
|
|
|
|
826
|
$uri->fragment(undef); |
93
|
|
|
|
|
|
|
|
94
|
142
|
|
|
|
|
1675
|
my $schema = $self->cache_resolve($uri); |
95
|
142
|
|
|
|
|
732
|
return $self->fragment_resolve($origin_uri, $schema); |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# self - URIResolver |
99
|
|
|
|
|
|
|
# uri - URI |
100
|
|
|
|
|
|
|
# return schema |
101
|
|
|
|
|
|
|
sub cache_resolve { |
102
|
142
|
|
|
142
|
0
|
295
|
my ($self, $uri) = @_; |
103
|
|
|
|
|
|
|
|
104
|
142
|
|
|
|
|
267
|
my $scheme = $uri->scheme; |
105
|
|
|
|
|
|
|
|
106
|
142
|
50
|
|
|
|
1515
|
return $self->cache->{$uri->as_string} if exists $self->cache->{$uri->as_string}; |
107
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
0
|
my ($response, $mime_type) = get_resource($self->scheme_handlers, $uri->as_string); |
109
|
0
|
|
|
|
|
0
|
my $schema = decode_content($response, $mime_type, $uri->as_string); |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
0
|
$self->cache->{$uri->as_string} = $schema; |
112
|
|
|
|
|
|
|
|
113
|
0
|
0
|
|
|
|
0
|
$self->cache_id($uri, $schema) if $self->validator->using_id_with_ref; |
114
|
|
|
|
|
|
|
|
115
|
0
|
|
|
|
|
0
|
return $schema; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# self - URIResolver |
119
|
|
|
|
|
|
|
# uri - URI |
120
|
|
|
|
|
|
|
# schema - HASH/ARRAY |
121
|
|
|
|
|
|
|
# return (scope|string, schema) |
122
|
|
|
|
|
|
|
sub fragment_resolve { |
123
|
142
|
|
|
142
|
0
|
266
|
my ($self, $uri, $schema) = @_; |
124
|
142
|
50
|
|
|
|
223
|
return ($uri->as_string, $self->cache->{$uri->as_string}) if exists $self->cache->{$uri->as_string}; |
125
|
|
|
|
|
|
|
|
126
|
142
|
|
|
|
|
799
|
my $enc = Encode::find_encoding("UTF-8"); |
127
|
142
|
|
|
|
|
4415
|
my $fragment = $enc->decode(uri_unescape($uri->fragment), 1); |
128
|
|
|
|
|
|
|
|
129
|
142
|
|
|
|
|
3149
|
my $pointer = json_pointer->new( |
130
|
|
|
|
|
|
|
scope => $uri->as_string, |
131
|
|
|
|
|
|
|
value => $schema, |
132
|
|
|
|
|
|
|
validator => $self->validator |
133
|
|
|
|
|
|
|
); |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# try to use fragment as json pointer |
136
|
142
|
|
|
|
|
377
|
$pointer = $pointer->get($fragment); |
137
|
142
|
|
|
|
|
299
|
my $subschema = $pointer->value; |
138
|
142
|
|
|
|
|
249
|
my $current_scope = $pointer->scope; |
139
|
|
|
|
|
|
|
|
140
|
142
|
|
|
|
|
287
|
$self->cache->{$uri->as_string} = $subschema; |
141
|
|
|
|
|
|
|
|
142
|
142
|
|
|
|
|
956
|
return ($current_scope, $subschema); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# self - URIResolver |
146
|
|
|
|
|
|
|
# uri - URI |
147
|
|
|
|
|
|
|
# schema - HASH/ARRAY |
148
|
|
|
|
|
|
|
sub cache_id { |
149
|
280
|
|
|
280
|
0
|
22814
|
my ($self, $uri, $schema) = @_; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# try to find id/$id and cache it to properly handle links in $ref |
152
|
|
|
|
|
|
|
# https://json-schema.org/understanding-json-schema/structuring.html#using-id-with-ref |
153
|
|
|
|
|
|
|
|
154
|
280
|
|
|
|
|
487
|
my $scopes = [$uri]; |
155
|
280
|
|
|
|
|
638
|
$self->cache_id_dfs($schema, $scopes); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# self - URIResolver |
159
|
|
|
|
|
|
|
# schema - HASH/ARRAY |
160
|
|
|
|
|
|
|
# scopes - [URI, ...] |
161
|
|
|
|
|
|
|
sub cache_id_dfs { |
162
|
3504
|
|
|
3504
|
0
|
4843
|
my ($self, $schema, $scopes) = @_; |
163
|
3504
|
50
|
|
|
|
5305
|
return unless ref $schema eq 'HASH'; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# skip all fields (id field too) if $ref present (draft 4-7) |
166
|
3504
|
100
|
|
|
|
6389
|
return if exists $schema->{'$ref'}; |
167
|
|
|
|
|
|
|
|
168
|
2480
|
100
|
66
|
|
|
3595
|
if (exists $schema->{$self->validator->ID_FIELD} && !ref $schema->{$self->validator->ID_FIELD}) { |
169
|
47
|
|
|
|
|
100
|
my $id = URI->new($schema->{$self->validator->ID_FIELD}); |
170
|
47
|
|
|
|
|
4051
|
my $scope = $scopes->[-1]; |
171
|
|
|
|
|
|
|
|
172
|
47
|
50
|
33
|
|
|
359
|
$id = ($scope && $scope->as_string) ? $id->abs($scope) : $id; |
173
|
|
|
|
|
|
|
|
174
|
47
|
|
|
|
|
3778
|
$self->cache->{$id->as_string} = $schema; |
175
|
47
|
|
|
|
|
255
|
push @$scopes, $id; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
2480
|
|
|
|
|
5466
|
for my $k (keys %$schema) { |
179
|
4313
|
100
|
100
|
|
|
7974
|
if ($SEARCH_ID->{value}{$k} && ref $schema->{$k} eq 'HASH') { |
180
|
405
|
|
|
|
|
689
|
$self->cache_id_dfs($schema->{$k}, $scopes); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
4313
|
100
|
100
|
|
|
7303
|
if ($SEARCH_ID->{arr_value}{$k} && ref $schema->{$k} eq 'ARRAY') { |
184
|
289
|
|
|
|
|
364
|
for my $value (@{$schema->{$k}}) { |
|
289
|
|
|
|
|
469
|
|
185
|
609
|
50
|
|
|
|
969
|
next unless ref $value eq 'HASH'; |
186
|
609
|
|
|
|
|
895
|
$self->cache_id_dfs($value, $scopes); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
4313
|
100
|
66
|
|
|
7999
|
if ($SEARCH_ID->{kv_value}{$k} && ref $schema->{$k} eq 'HASH') { |
191
|
513
|
|
|
|
|
653
|
for my $kv_key (keys %{$schema->{$k}}) { |
|
513
|
|
|
|
|
1436
|
|
192
|
2245
|
|
|
|
|
2999
|
my $value = $schema->{$k}{$kv_key}; |
193
|
2245
|
100
|
|
|
|
3643
|
next unless ref $value eq 'HASH'; |
194
|
2210
|
|
|
|
|
3089
|
$self->cache_id_dfs($value, $scopes); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
2480
|
100
|
66
|
|
|
3871
|
if (exists $schema->{$self->validator->ID_FIELD} && !ref $schema->{$self->validator->ID_FIELD}) { |
200
|
47
|
|
|
|
|
112
|
pop @$scopes; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
1; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
__END__ |