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   42 use strict;
  6         15  
  6         184  
6 6     6   30 use warnings;
  6         10  
  6         133  
7 6     6   30 use URI;
  6         12  
  6         158  
8 6     6   33 use Carp 'croak';
  6         21  
  6         307  
9              
10 6     6   2652 use JSONSchema::Validator::Error 'error';
  6         16  
  6         329  
11 6     6   2650 use JSONSchema::Validator::JSONPointer 'json_pointer';
  6         83  
  6         334  
12 6     6   3260 use JSONSchema::Validator::Constraints::Draft4;
  6         26  
  6         251  
13 6     6   3459 use JSONSchema::Validator::URIResolver;
  6         23  
  6         240  
14 6     6   43 use JSONSchema::Validator::Util qw(is_type);
  6         22  
  6         267  
15              
16 6     6   40 use constant SPECIFICATION => 'Draft4';
  6         23  
  6         414  
17 6     6   41 use constant ID => 'http://json-schema.org/draft-04/schema#';
  6         14  
  6         271  
18 6     6   37 use constant ID_FIELD => 'id';
  6         16  
  6         6379  
19              
20             sub create {
21 321     321 0 799 my ($class, %params) = @_;
22              
23 321 50       958 croak 'schema is required' unless exists $params{schema};
24              
25 321         616 my $schema = $params{schema};
26 321   100     1314 my $using_id_with_ref = $params{using_id_with_ref} // 1;
27              
28 321   50     1043 my $scheme_handlers = $params{scheme_handlers} // {};
29              
30 321         1310 my $self = {
31             schema => $schema,
32             errors => [],
33             scopes => [],
34             using_id_with_ref => $using_id_with_ref
35             };
36              
37 321         715 bless $self, $class;
38              
39             # schema may be boolean value according to json schema draft6
40 321         588 my $base_uri = $params{base_uri};
41 321 100 100     2200 $base_uri //= $schema->{$self->ID_FIELD} if ref $schema eq 'HASH';
42 321   100     1383 $base_uri //= '';
43 321         768 $self->{base_uri} = $base_uri;
44              
45 321         1552 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         688 $self->{resolver} = $resolver;
52              
53 321         493 push @{$self->scopes}, $base_uri;
  321         778  
54              
55 321         846 return $self;
56             }
57              
58             sub new {
59 239     239 1 294691 my ($class, %params) = @_;
60              
61 239         861 my $self = $class->create(%params);
62              
63 239   100     1556 my $constraints = JSONSchema::Validator::Constraints::Draft4->new(validator => $self, strict => $params{strict} // 1);
64 239         497 $self->{constraints} = $constraints;
65              
66 239         676 return $self;
67             }
68              
69 371     371 1 1105 sub schema { shift->{schema} }
70 7462     7462 0 25365 sub constraints { shift->{constraints} }
71 450     450 0 1612 sub resolver { shift->{resolver} }
72 1295     1295 0 3450 sub scopes { shift->{scopes} }
73 536     536 0 4271 sub scope { shift->{scopes}[-1] }
74 0     0 0 0 sub base_uri { shift->{base_uri} }
75 2037     2037 1 7628 sub using_id_with_ref { shift->{using_id_with_ref} }
76              
77             sub validate_schema {
78 294     294 1 13740 my ($self, $instance, %params) = @_;
79              
80 294   66     1073 my $schema = $params{schema} // $self->schema;
81 294   50     1083 my $instance_path = $params{instance_path} // '/';
82 294   50     837 my $schema_path = $params{schema_path} // '/';
83 294         455 my $scope = $params{scope};
84              
85 294 50       650 croak 'No schema specified' unless defined $schema;
86              
87 294 50       598 push @{$self->scopes}, $scope if $scope;
  0         0  
88              
89 294         489 my $errors = [];
90 294         1048 my $result = $self->_validate_schema($instance, $schema, $instance_path, $schema_path, {errors => $errors});
91              
92 294 50       830 pop @{$self->scopes} if $scope;
  0         0  
93              
94 294         1142 return $result, $errors;
95             }
96              
97             sub _validate_schema {
98 2207     2207   5870 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       5726 if (is_type($schema, 'boolean', 1)) {
102 14 100       45 return 1 if $schema;
103 1         10 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     6857 my $apply_scope = $params{apply_scope} // 1;
112              
113 2193         4034 my $is_exists_ref = exists $schema->{'$ref'};
114              
115 2193         5477 my $id = $schema->{$self->ID_FIELD};
116 2193 50 100     5060 if ($id && $apply_scope && $self->using_id_with_ref && !$is_exists_ref) {
      66        
      66        
117 39         81 my $uri = $id;
118 39 50       97 $uri = URI->new($id)->abs($self->scope)->as_string if $self->scope;
119 39         962 push @{$self->scopes}, $uri;
  39         88  
120             }
121              
122 2193         4805 my @schema_keys = $self->_schema_keys($schema, $instance_path, $data);
123              
124 2193         3648 my $result = 1;
125 2193         3943 for my $k (@schema_keys) {
126 4075         7108 my $v = $schema->{$k};
127              
128 4075 100       7789 my $method = $k eq '$ref' ? 'ref' : $k;
129 4075 100       7747 next unless my $constraint = $self->constraints->can($method);
130              
131 3387         8347 my $spath = json_pointer->append($schema_path, $k);
132              
133 3387         5992 my $r = eval {
134 3387         6317 $self->constraints->$constraint($instance, $v, $schema, $instance_path, $spath, $data);
135             };
136 3387 50       7483 push @{$data->{errors}}, error(
  0         0  
137             message => "exception: $@",
138             instance_path => $instance_path,
139             schema_path => $spath
140             ) if $@;
141 3387 100       8451 $result = 0 unless $r;
142             }
143              
144 2193 50 100     4631 pop @{$self->scopes} if $id && $apply_scope && $self->using_id_with_ref && !$is_exists_ref;
  39   66     127  
      66        
145 2193         6102 return $result;
146             }
147              
148             sub _schema_keys {
149 1694     1694   3437 my ($self, $schema, $instance_path, $data) = @_;
150             # if ref exists other preperties MUST be ignored
151 1694 100       3754 return '$ref' if $schema->{'$ref'};
152 1368         4048 return keys %$schema;
153             }
154              
155             1;
156              
157             __END__