File Coverage

blib/lib/JSONSchema/Validator/Draft4.pm
Criterion Covered Total %
statement 105 109 96.3
branch 24 32 75.0
condition 30 38 78.9
subroutine 23 24 95.8
pod 4 10 40.0
total 186 213 87.3


line stmt bran cond sub pod time code
1             package JSONSchema::Validator::Draft4;
2              
3             # ABSTRACT: Validator for JSON Schema Draft4
4              
5 6     6   36 use strict;
  6         11  
  6         171  
6 6     6   27 use warnings;
  6         13  
  6         148  
7 6     6   25 use URI;
  6         12  
  6         145  
8 6     6   27 use Carp 'croak';
  6         13  
  6         262  
9              
10 6     6   2267 use JSONSchema::Validator::Error 'error';
  6         13  
  6         283  
11 6     6   2230 use JSONSchema::Validator::JSONPointer 'json_pointer';
  6         84  
  6         295  
12 6     6   2853 use JSONSchema::Validator::Constraints::Draft4;
  6         24  
  6         240  
13 6     6   3119 use JSONSchema::Validator::URIResolver;
  6         21  
  6         223  
14 6     6   35 use JSONSchema::Validator::Util qw(is_type);
  6         14  
  6         243  
15              
16 6     6   34 use constant SPECIFICATION => 'Draft4';
  6         14  
  6         379  
17 6     6   29 use constant ID => 'http://json-schema.org/draft-04/schema#';
  6         12  
  6         223  
18 6     6   30 use constant ID_FIELD => 'id';
  6         11  
  6         5158  
19              
20             sub create {
21 321     321 0 694 my ($class, %params) = @_;
22              
23 321 50       820 croak 'schema is required' unless exists $params{schema};
24              
25 321         498 my $schema = $params{schema};
26 321   100     1020 my $using_id_with_ref = $params{using_id_with_ref} // 1;
27              
28 321   50     962 my $scheme_handlers = $params{scheme_handlers} // {};
29              
30 321         1136 my $self = {
31             schema => $schema,
32             errors => [],
33             scopes => [],
34             using_id_with_ref => $using_id_with_ref
35             };
36              
37 321         571 bless $self, $class;
38              
39             # schema may be boolean value according to json schema draft6
40 321         423 my $base_uri = $params{base_uri};
41 321 100 100     1958 $base_uri //= $schema->{$self->ID_FIELD} if ref $schema eq 'HASH';
42 321   100     987 $base_uri //= '';
43 321         675 $self->{base_uri} = $base_uri;
44              
45 321         1173 my $resolver = JSONSchema::Validator::URIResolver->new(
46             validator => $self,
47             base_uri => $base_uri,
48             schema => $schema,
49             scheme_handlers => $scheme_handlers
50             );
51 321         515 $self->{resolver} = $resolver;
52              
53 321         407 push @{$self->scopes}, $base_uri;
  321         608  
54              
55 321         702 return $self;
56             }
57              
58             sub new {
59 239     239 1 288857 my ($class, %params) = @_;
60              
61 239         773 my $self = $class->create(%params);
62              
63 239   100     1250 my $constraints = JSONSchema::Validator::Constraints::Draft4->new(validator => $self, strict => $params{strict} // 1);
64 239         404 $self->{constraints} = $constraints;
65              
66 239         526 return $self;
67             }
68              
69 373     373 1 968 sub schema { shift->{schema} }
70 7540     7540 0 20847 sub constraints { shift->{constraints} }
71 454     454 0 2143 sub resolver { shift->{resolver} }
72 1299     1299 0 2661 sub scopes { shift->{scopes} }
73 538     538 0 3594 sub scope { shift->{scopes}[-1] }
74 0     0 0 0 sub base_uri { shift->{base_uri} }
75 2196     2196 1 6766 sub using_id_with_ref { shift->{using_id_with_ref} }
76              
77             sub validate_schema {
78 294     294 1 15669 my ($self, $instance, %params) = @_;
79              
80 294   66     769 my $schema = $params{schema} // $self->schema;
81 294   50     764 my $instance_path = $params{instance_path} // '/';
82 294   50     688 my $schema_path = $params{schema_path} // '/';
83 294         372 my $scope = $params{scope};
84              
85 294 50       609 croak 'No schema specified' unless defined $schema;
86              
87 294 50       513 push @{$self->scopes}, $scope if $scope;
  0         0  
88              
89 294         394 my $errors = [];
90 294         798 my $result = $self->_validate_schema($instance, $schema, $instance_path, $schema_path, {errors => $errors});
91              
92 294 50       694 pop @{$self->scopes} if $scope;
  0         0  
93              
94 294         920 return $result, $errors;
95             }
96              
97             sub _validate_schema {
98 2232     2232   5101 my ($self, $instance, $schema, $instance_path, $schema_path, $data, %params) = @_;
99              
100             # for json schema draft 6 which allow boolean value for schema
101 2232 100       4626 if (is_type($schema, 'boolean', 1)) {
102 14 100       50 return 1 if $schema;
103 1         10 push @{$data->{errors}}, error(
  1         4  
104             message => 'Schema with value "false" does not allow anything',
105             instance_path => $instance_path,
106             schema_path => $schema_path
107             );
108 1         3 return 0;
109             }
110              
111 2218   100     5261 my $apply_scope = $params{apply_scope} // 1;
112              
113 2218         3317 my $is_exists_ref = exists $schema->{'$ref'};
114              
115 2218         4465 my $id = $schema->{$self->ID_FIELD};
116 2218 50 100     3930 if ($id && $apply_scope && $self->using_id_with_ref && !$is_exists_ref) {
      66        
      66        
117 39         60 my $uri = $id;
118 39 50       85 $uri = URI->new($id)->abs($self->scope)->as_string if $self->scope;
119 39         836 push @{$self->scopes}, $uri;
  39         75  
120             }
121              
122 2218         3846 my @schema_keys = $self->_schema_keys($schema, $instance_path, $data);
123              
124 2218         3012 my $result = 1;
125 2218         3428 for my $k (@schema_keys) {
126 4114         5711 my $v = $schema->{$k};
127              
128 4114 100       6537 my $method = $k eq '$ref' ? 'ref' : $k;
129 4114 100       6259 next unless my $constraint = $self->constraints->can($method);
130              
131 3426 100       6955 my $spath = json_pointer->append($schema_path, $k eq '$ref' ? "$k($v)" : $k);
132              
133 3426         4983 my $r = eval {
134 3426         5158 $self->constraints->$constraint($instance, $v, $schema, $instance_path, $spath, $data);
135             };
136 3426 50       5749 push @{$data->{errors}}, error(
  0         0  
137             message => "exception: $@",
138             instance_path => $instance_path,
139             schema_path => $spath
140             ) if $@;
141 3426 100       7048 $result = 0 unless $r;
142             }
143              
144 2218 50 100     3991 pop @{$self->scopes} if $id && $apply_scope && $self->using_id_with_ref && !$is_exists_ref;
  39   66     68  
      66        
145 2218         4840 return $result;
146             }
147              
148             sub _schema_keys {
149 1694     1694   2701 my ($self, $schema, $instance_path, $data) = @_;
150             # if ref exists other preperties MUST be ignored
151 1694 100       3138 return '$ref' if $schema->{'$ref'};
152 1368         3245 return keys %$schema;
153             }
154              
155             1;
156              
157             __END__