File Coverage

blib/lib/JSONSchema/Validator.pm
Criterion Covered Total %
statement 92 92 100.0
branch 17 24 70.8
condition 18 25 72.0
subroutine 18 18 100.0
pod 4 8 50.0
total 149 167 89.2


line stmt bran cond sub pod time code
1             package JSONSchema::Validator;
2              
3             # ABSTRACT: Validator for JSON Schema Draft4/Draft6/Draft7 and OpenAPI Specification 3.0
4              
5 6     6   370097 use strict;
  6         64  
  6         149  
6 6     6   26 use warnings;
  6         9  
  6         115  
7 6     6   2267 use URI::file;
  6         66910  
  6         188  
8 6     6   39 use Carp 'croak';
  6         13  
  6         240  
9 6     6   30 use Cwd;
  6         10  
  6         233  
10              
11 6     6   2740 use JSONSchema::Validator::Draft4;
  6         20  
  6         281  
12 6     6   2698 use JSONSchema::Validator::Draft6;
  6         17  
  6         156  
13 6     6   2378 use JSONSchema::Validator::Draft7;
  6         15  
  6         146  
14 6     6   2465 use JSONSchema::Validator::OAS30;
  6         21  
  6         199  
15 6     6   42 use JSONSchema::Validator::Util qw(get_resource decode_content read_file);
  6         15  
  6         5230  
16              
17             our $VERSION = '0.011';
18              
19             my $SPECIFICATIONS = {
20             JSONSchema::Validator::OAS30::ID => JSONSchema::Validator::OAS30::SPECIFICATION,
21             JSONSchema::Validator::Draft4::ID => JSONSchema::Validator::Draft4::SPECIFICATION,
22             JSONSchema::Validator::Draft6::ID => JSONSchema::Validator::Draft6::SPECIFICATION,
23             JSONSchema::Validator::Draft7::ID => JSONSchema::Validator::Draft7::SPECIFICATION
24             };
25              
26             our $JSON_SCHEMA_VALIDATORS = ['JSONSchema::Validator::Draft4', 'JSONSchema::Validator::Draft6', 'JSONSchema::Validator::Draft7'];
27             our $OAS_VALIDATORS = ['JSONSchema::Validator::OAS30'];
28              
29              
30             sub new {
31 25     25 1 10316 my ($class, %params) = @_;
32              
33 25         66 my $resource = delete $params{resource};
34 25   50     107 my $validate_schema = delete($params{validate_schema}) // 1;
35 25         48 my $schema = delete $params{schema};
36 25         46 my $base_uri = delete $params{base_uri};
37 25         47 my $specification = delete $params{specification};
38              
39 25 100 66     136 $schema = resource_schema($resource, \%params) if !$schema && $resource;
40 25 50       73 croak 'resource or schema must be specified' unless defined $schema;
41              
42 25   100     97 my $validator_class = find_validator($specification // schema_specification($schema));
43 25 100       978 croak 'unknown specification' unless $validator_class;
44              
45 19 50       54 if ($validate_schema) {
46 19         87 my ($result, $errors) = $class->validate_resource_schema($schema, $validator_class->SPECIFICATION);
47 19 100       128 croak "invalid schema:\n" . join "\n", @$errors unless $result;
48             }
49              
50             # schema may be boolean value according to json schema draft6
51 15 50       48 if (ref $schema eq 'HASH') {
52 15   66     102 $base_uri //= $resource || $schema->{'$id'} || $schema->{id};
      66        
53             }
54              
55 15         83 return $validator_class->new(schema => $schema, base_uri => $base_uri, %params);
56             }
57              
58              
59             sub validate_paths {
60 2     2 1 3233 my ($class, $globs) = @_;
61 2         6 my $results = {};
62 2         7 for my $glob (@$globs) {
63 2         220 my @resources = map { Cwd::abs_path($_) } glob $glob;
  6         297  
64 2         9 for my $resource (@resources) {
65 6         29 my $uri = URI::file->new($resource)->as_string;
66 6         2510 my ($result, $errors) = $class->validate_resource($uri);
67 6         23 $results->{$resource} = [$result, $errors];
68             }
69             }
70 2         6 return $results;
71             }
72              
73              
74             sub validate_resource {
75 6     6 1 13 my ($class, $resource, %params) = @_;
76 6         13 my $schema_to_validate = resource_schema($resource, \%params);
77              
78 6         12 my $validator_class = find_validator(schema_specification($schema_to_validate));
79 6 50       13 croak "unknown specification of resource $resource" unless $validator_class;
80              
81 6         21 return $class->validate_resource_schema($schema_to_validate, $validator_class->SPECIFICATION);
82             }
83              
84              
85             sub validate_resource_schema {
86 25     25 1 71 my ($class, $schema_to_validate, $schema_specification) = @_;
87              
88 25         64 my $schema = read_specification($schema_specification);
89 25         73 my $meta_schema = $schema->{'$schema'};
90              
91 25   33     83 my $meta_schema_specification = $SPECIFICATIONS->{$meta_schema} // $SPECIFICATIONS->{$meta_schema . '#'};
92 25 50       53 croak "unknown meta schema: $meta_schema" unless $meta_schema_specification;
93              
94 25         61 my $validator_class = find_validator($meta_schema_specification);
95 25 50       77 croak "can't find validator by meta schema: $meta_schema" unless $validator_class;
96              
97 25         104 my $validator = $validator_class->new(schema => $schema);
98 25         92 my ($result, $errors) = $validator->validate_schema($schema_to_validate);
99 25         116 return ($result, $errors);
100             }
101              
102             sub read_specification {
103 25     25 0 44 my $filename = shift;
104 25         48 my $curret_filepath = __FILE__;
105 25         150 my $schema_filepath = ($curret_filepath =~ s/\.pm$//r) . '/schemas/' . lc($filename) . '.json';
106 25         87 my ($content, $mime_type) = read_file($schema_filepath);
107 25         98 return decode_content($content, $mime_type, $schema_filepath);
108             }
109              
110             sub resource_schema {
111 20     20 0 49 my ($resource, $params) = @_;
112 20         93 my ($response, $mime_type) = get_resource($params->{scheme_handlers}, $resource);
113 20         86 my $schema = decode_content($response, $mime_type, $resource);
114 20         45 return $schema;
115             }
116              
117             sub find_validator {
118 56     56 0 91 my $specification = shift;
119 56   100     138 my ($validator_class) = grep { lc($_->SPECIFICATION) eq lc($specification // '') } @$JSON_SCHEMA_VALIDATORS, @$OAS_VALIDATORS;
  224         1142  
120 56         116 return $validator_class;
121             }
122              
123             sub schema_specification {
124 24     24 0 51 my $schema = shift;
125 24 50       81 return if ref $schema ne 'HASH';
126              
127 24         51 my $meta_schema = $schema->{'$schema'};
128 24 100 66     98 my $specification = $meta_schema ? $SPECIFICATIONS->{$meta_schema} // $SPECIFICATIONS->{$meta_schema . '#'} : undef;
129              
130 24 100 100     92 if (!$specification && $schema->{openapi}) {
131 3         11 my @vers = split /\./, $schema->{openapi};
132 3         9 $specification = 'OAS' . $vers[0] . $vers[1];
133             }
134              
135 24         82 return $specification;
136             }
137              
138             1;
139              
140             __END__