File Coverage

blib/lib/JSONSchema/Validator/Draft4.pm
Criterion Covered Total %
statement 105 109 96.3
branch 22 30 73.3
condition 30 38 78.9
subroutine 23 24 95.8
pod 4 10 40.0
total 184 211 87.2


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   41 use strict;
  6         13  
  6         173  
6 6     6   36 use warnings;
  6         10  
  6         165  
7 6     6   28 use URI;
  6         11  
  6         153  
8 6     6   31 use Carp 'croak';
  6         12  
  6         291  
9              
10 6     6   2458 use JSONSchema::Validator::Error 'error';
  6         16  
  6         328  
11 6     6   2579 use JSONSchema::Validator::JSONPointer 'json_pointer';
  6         96  
  6         335  
12 6     6   3223 use JSONSchema::Validator::Constraints::Draft4;
  6         21  
  6         251  
13 6     6   3144 use JSONSchema::Validator::URIResolver;
  6         18  
  6         240  
14 6     6   45 use JSONSchema::Validator::Util qw(is_type);
  6         16  
  6         256  
15              
16 6     6   33 use constant SPECIFICATION => 'Draft4';
  6         13  
  6         399  
17 6     6   36 use constant ID => 'http://json-schema.org/draft-04/schema#';
  6         14  
  6         250  
18 6     6   34 use constant ID_FIELD => 'id';
  6         11  
  6         5661  
19              
20             sub create {
21 321     321 0 712 my ($class, %params) = @_;
22              
23 321 50       926 croak 'schema is required' unless exists $params{schema};
24              
25 321         549 my $schema = $params{schema};
26 321   100     1144 my $using_id_with_ref = $params{using_id_with_ref} // 1;
27              
28 321   50     1053 my $scheme_handlers = $params{scheme_handlers} // {};
29              
30 321         1160 my $self = {
31             schema => $schema,
32             errors => [],
33             scopes => [],
34             using_id_with_ref => $using_id_with_ref
35             };
36              
37 321         642 bless $self, $class;
38              
39             # schema may be boolean value according to json schema draft6
40 321         468 my $base_uri = $params{base_uri};
41 321 100 100     2081 $base_uri //= $schema->{$self->ID_FIELD} if ref $schema eq 'HASH';
42 321   100     1162 $base_uri //= '';
43 321         691 $self->{base_uri} = $base_uri;
44              
45 321         1215 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         699 $self->{resolver} = $resolver;
52              
53 321         477 push @{$self->scopes}, $base_uri;
  321         787  
54              
55 321         800 return $self;
56             }
57              
58             sub new {
59 239     239 1 284026 my ($class, %params) = @_;
60              
61 239         778 my $self = $class->create(%params);
62              
63 239   100     1298 my $constraints = JSONSchema::Validator::Constraints::Draft4->new(validator => $self, strict => $params{strict} // 1);
64 239         470 $self->{constraints} = $constraints;
65              
66 239         638 return $self;
67             }
68              
69 371     371 1 1044 sub schema { shift->{schema} }
70 7462     7462 0 22360 sub constraints { shift->{constraints} }
71 450     450 0 1348 sub resolver { shift->{resolver} }
72 1295     1295 0 3063 sub scopes { shift->{scopes} }
73 536     536 0 4229 sub scope { shift->{scopes}[-1] }
74 0     0 0 0 sub base_uri { shift->{base_uri} }
75 2037     2037 1 7184 sub using_id_with_ref { shift->{using_id_with_ref} }
76              
77             sub validate_schema {
78 294     294 1 21559 my ($self, $instance, %params) = @_;
79              
80 294   66     911 my $schema = $params{schema} // $self->schema;
81 294   50     1168 my $instance_path = $params{instance_path} // '/';
82 294   50     770 my $schema_path = $params{schema_path} // '/';
83 294         490 my $scope = $params{scope};
84              
85 294 50       604 croak 'No schema specified' unless defined $schema;
86              
87 294 50       542 push @{$self->scopes}, $scope if $scope;
  0         0  
88              
89 294         479 my $errors = [];
90 294         905 my $result = $self->_validate_schema($instance, $schema, $instance_path, $schema_path, {errors => $errors});
91              
92 294 50       812 pop @{$self->scopes} if $scope;
  0         0  
93              
94 294         1103 return $result, $errors;
95             }
96              
97             sub _validate_schema {
98 2207     2207   5934 my ($self, $instance, $schema, $instance_path, $schema_path, $data, %params) = @_;
99              
100             # for json schema draft 6 which allow boolean value for schema
101 2207 100       5466 if (is_type($schema, 'boolean', 1)) {
102 14 100       50 return 1 if $schema;
103 1         11 push @{$data->{errors}}, error(
  1         6  
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 2193   100     5898 my $apply_scope = $params{apply_scope} // 1;
112              
113 2193         3797 my $is_exists_ref = exists $schema->{'$ref'};
114              
115 2193         5177 my $id = $schema->{$self->ID_FIELD};
116 2193 50 100     4491 if ($id && $apply_scope && $self->using_id_with_ref && !$is_exists_ref) {
      66        
      66        
117 39         69 my $uri = $id;
118 39 50       109 $uri = URI->new($id)->abs($self->scope)->as_string if $self->scope;
119 39         961 push @{$self->scopes}, $uri;
  39         87  
120             }
121              
122 2193         4303 my @schema_keys = $self->_schema_keys($schema, $instance_path, $data);
123              
124 2193         3255 my $result = 1;
125 2193         3798 for my $k (@schema_keys) {
126 4075         6500 my $v = $schema->{$k};
127              
128 4075 100       7407 my $method = $k eq '$ref' ? 'ref' : $k;
129 4075 100       7123 next unless my $constraint = $self->constraints->can($method);
130              
131 3387         7829 my $spath = json_pointer->append($schema_path, $k);
132              
133 3387         5591 my $r = eval {
134 3387         5720 $self->constraints->$constraint($instance, $v, $schema, $instance_path, $spath, $data);
135             };
136 3387 50       6514 push @{$data->{errors}}, error(
  0         0  
137             message => "exception: $@",
138             instance_path => $instance_path,
139             schema_path => $spath
140             ) if $@;
141 3387 100       7575 $result = 0 unless $r;
142             }
143              
144 2193 50 100     4258 pop @{$self->scopes} if $id && $apply_scope && $self->using_id_with_ref && !$is_exists_ref;
  39   66     83  
      66        
145 2193         5399 return $result;
146             }
147              
148             sub _schema_keys {
149 1694     1694   3050 my ($self, $schema, $instance_path, $data) = @_;
150             # if ref exists other preperties MUST be ignored
151 1694 100       3532 return '$ref' if $schema->{'$ref'};
152 1368         3728 return keys %$schema;
153             }
154              
155             1;
156              
157             __END__