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   441537 use strict;
  6         66  
  6         176  
6 6     6   31 use warnings;
  6         11  
  6         141  
7 6     6   2837 use URI::file;
  6         80555  
  6         234  
8 6     6   42 use Carp 'croak';
  6         12  
  6         260  
9 6     6   36 use Cwd;
  6         13  
  6         245  
10              
11 6     6   2791 use JSONSchema::Validator::Draft4;
  6         24  
  6         223  
12 6     6   2921 use JSONSchema::Validator::Draft6;
  6         19  
  6         179  
13 6     6   2735 use JSONSchema::Validator::Draft7;
  6         29  
  6         495  
14 6     6   3279 use JSONSchema::Validator::OAS30;
  6         23  
  6         206  
15 6     6   77 use JSONSchema::Validator::Util qw(get_resource decode_content read_file);
  6         14  
  6         6283  
16              
17             our $VERSION = '0.009';
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 10415 my ($class, %params) = @_;
32              
33 25         66 my $resource = delete $params{resource};
34 25   50     110 my $validate_schema = delete($params{validate_schema}) // 1;
35 25         47 my $schema = delete $params{schema};
36 25         43 my $base_uri = delete $params{base_uri};
37 25         42 my $specification = delete $params{specification};
38              
39 25 100 66     110 $schema = resource_schema($resource, \%params) if !$schema && $resource;
40 25 50       60 croak 'resource or schema must be specified' unless defined $schema;
41              
42 25   100     84 my $validator_class = find_validator($specification // schema_specification($schema));
43 25 100       841 croak 'unknown specification' unless $validator_class;
44              
45 19 50       45 if ($validate_schema) {
46 19         73 my ($result, $errors) = $class->validate_resource_schema($schema, $validator_class->SPECIFICATION);
47 19 100       146 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       38 if (ref $schema eq 'HASH') {
52 15   66     92 $base_uri //= $resource || $schema->{'$id'} || $schema->{id};
      66        
53             }
54              
55 15         71 return $validator_class->new(schema => $schema, base_uri => $base_uri, %params);
56             }
57              
58              
59             sub validate_paths {
60 2     2 1 3017 my ($class, $globs) = @_;
61 2         4 my $results = {};
62 2         7 for my $glob (@$globs) {
63 2         282 my @resources = map { Cwd::abs_path($_) } glob $glob;
  6         383  
64 2         10 for my $resource (@resources) {
65 6         36 my $uri = URI::file->new($resource)->as_string;
66 6         3019 my ($result, $errors) = $class->validate_resource($uri);
67 6         30 $results->{$resource} = [$result, $errors];
68             }
69             }
70 2         7 return $results;
71             }
72              
73              
74             sub validate_resource {
75 6     6 1 17 my ($class, $resource, %params) = @_;
76 6         14 my $schema_to_validate = resource_schema($resource, \%params);
77              
78 6         14 my $validator_class = find_validator(schema_specification($schema_to_validate));
79 6 50       15 croak "unknown specification of resource $resource" unless $validator_class;
80              
81 6         27 return $class->validate_resource_schema($schema_to_validate, $validator_class->SPECIFICATION);
82             }
83              
84              
85             sub validate_resource_schema {
86 25     25 1 55 my ($class, $schema_to_validate, $schema_specification) = @_;
87              
88 25         55 my $schema = read_specification($schema_specification);
89 25         61 my $meta_schema = $schema->{'$schema'};
90              
91 25   33     84 my $meta_schema_specification = $SPECIFICATIONS->{$meta_schema} // $SPECIFICATIONS->{$meta_schema . '#'};
92 25 50       58 croak "unknown meta schema: $meta_schema" unless $meta_schema_specification;
93              
94 25         68 my $validator_class = find_validator($meta_schema_specification);
95 25 50       54 croak "can't find validator by meta schema: $meta_schema" unless $validator_class;
96              
97 25         93 my $validator = $validator_class->new(schema => $schema);
98 25         108 my ($result, $errors) = $validator->validate_schema($schema_to_validate);
99 25         102 return ($result, $errors);
100             }
101              
102             sub read_specification {
103 25     25 0 46 my $filename = shift;
104 25         46 my $curret_filepath = __FILE__;
105 25         173 my $schema_filepath = ($curret_filepath =~ s/\.pm$//r) . '/schemas/' . lc($filename) . '.json';
106 25         84 my ($content, $mime_type) = read_file($schema_filepath);
107 25         106 return decode_content($content, $mime_type, $schema_filepath);
108             }
109              
110             sub resource_schema {
111 20     20 0 52 my ($resource, $params) = @_;
112 20         89 my ($response, $mime_type) = get_resource($params->{scheme_handlers}, $resource);
113 20         99 my $schema = decode_content($response, $mime_type, $resource);
114 20         47 return $schema;
115             }
116              
117             sub find_validator {
118 56     56 0 101 my $specification = shift;
119 56   100     143 my ($validator_class) = grep { lc($_->SPECIFICATION) eq lc($specification // '') } @$JSON_SCHEMA_VALIDATORS, @$OAS_VALIDATORS;
  224         1243  
120 56         117 return $validator_class;
121             }
122              
123             sub schema_specification {
124 24     24 0 47 my $schema = shift;
125 24 50       70 return if ref $schema ne 'HASH';
126              
127 24         50 my $meta_schema = $schema->{'$schema'};
128 24 100 66     96 my $specification = $meta_schema ? $SPECIFICATIONS->{$meta_schema} // $SPECIFICATIONS->{$meta_schema . '#'} : undef;
129              
130 24 100 100     77 if (!$specification && $schema->{openapi}) {
131 3         14 my @vers = split /\./, $schema->{openapi};
132 3         11 $specification = 'OAS' . $vers[0] . $vers[1];
133             }
134              
135 24         71 return $specification;
136             }
137              
138             1;
139              
140             __END__