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   413989 use strict;
  6         68  
  6         170  
6 6     6   30 use warnings;
  6         12  
  6         136  
7 6     6   2594 use URI::file;
  6         82562  
  6         196  
8 6     6   46 use Carp 'croak';
  6         12  
  6         289  
9 6     6   33 use Cwd;
  6         13  
  6         246  
10              
11 6     6   2942 use JSONSchema::Validator::Draft4;
  6         20  
  6         193  
12 6     6   2895 use JSONSchema::Validator::Draft6;
  6         20  
  6         165  
13 6     6   2753 use JSONSchema::Validator::Draft7;
  6         20  
  6         175  
14 6     6   2825 use JSONSchema::Validator::OAS30;
  6         37  
  6         239  
15 6     6   50 use JSONSchema::Validator::Util qw(get_resource decode_content read_file);
  6         14  
  6         5927  
16              
17             our $VERSION = '0.010';
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 10244 my ($class, %params) = @_;
32              
33 25         70 my $resource = delete $params{resource};
34 25   50     117 my $validate_schema = delete($params{validate_schema}) // 1;
35 25         62 my $schema = delete $params{schema};
36 25         52 my $base_uri = delete $params{base_uri};
37 25         47 my $specification = delete $params{specification};
38              
39 25 100 66     134 $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     111 my $validator_class = find_validator($specification // schema_specification($schema));
43 25 100       1187 croak 'unknown specification' unless $validator_class;
44              
45 19 50       53 if ($validate_schema) {
46 19         98 my ($result, $errors) = $class->validate_resource_schema($schema, $validator_class->SPECIFICATION);
47 19 100       149 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       53 if (ref $schema eq 'HASH') {
52 15   66     108 $base_uri //= $resource || $schema->{'$id'} || $schema->{id};
      66        
53             }
54              
55 15         102 return $validator_class->new(schema => $schema, base_uri => $base_uri, %params);
56             }
57              
58              
59             sub validate_paths {
60 2     2 1 3440 my ($class, $globs) = @_;
61 2         6 my $results = {};
62 2         10 for my $glob (@$globs) {
63 2         326 my @resources = map { Cwd::abs_path($_) } glob $glob;
  6         334  
64 2         10 for my $resource (@resources) {
65 6         58 my $uri = URI::file->new($resource)->as_string;
66 6         2941 my ($result, $errors) = $class->validate_resource($uri);
67 6         33 $results->{$resource} = [$result, $errors];
68             }
69             }
70 2         12 return $results;
71             }
72              
73              
74             sub validate_resource {
75 6     6 1 20 my ($class, $resource, %params) = @_;
76 6         26 my $schema_to_validate = resource_schema($resource, \%params);
77              
78 6         19 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         33 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         72 my $schema = read_specification($schema_specification);
89 25         74 my $meta_schema = $schema->{'$schema'};
90              
91 25   33     96 my $meta_schema_specification = $SPECIFICATIONS->{$meta_schema} // $SPECIFICATIONS->{$meta_schema . '#'};
92 25 50       73 croak "unknown meta schema: $meta_schema" unless $meta_schema_specification;
93              
94 25         62 my $validator_class = find_validator($meta_schema_specification);
95 25 50       71 croak "can't find validator by meta schema: $meta_schema" unless $validator_class;
96              
97 25         119 my $validator = $validator_class->new(schema => $schema);
98 25         103 my ($result, $errors) = $validator->validate_schema($schema_to_validate);
99 25         140 return ($result, $errors);
100             }
101              
102             sub read_specification {
103 25     25 0 62 my $filename = shift;
104 25         49 my $curret_filepath = __FILE__;
105 25         183 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         114 return decode_content($content, $mime_type, $schema_filepath);
108             }
109              
110             sub resource_schema {
111 20     20 0 87 my ($resource, $params) = @_;
112 20         119 my ($response, $mime_type) = get_resource($params->{scheme_handlers}, $resource);
113 20         105 my $schema = decode_content($response, $mime_type, $resource);
114 20         51 return $schema;
115             }
116              
117             sub find_validator {
118 56     56 0 95 my $specification = shift;
119 56   100     168 my ($validator_class) = grep { lc($_->SPECIFICATION) eq lc($specification // '') } @$JSON_SCHEMA_VALIDATORS, @$OAS_VALIDATORS;
  224         1395  
120 56         132 return $validator_class;
121             }
122              
123             sub schema_specification {
124 24     24 0 62 my $schema = shift;
125 24 50       90 return if ref $schema ne 'HASH';
126              
127 24         54 my $meta_schema = $schema->{'$schema'};
128 24 100 66     125 my $specification = $meta_schema ? $SPECIFICATIONS->{$meta_schema} // $SPECIFICATIONS->{$meta_schema . '#'} : undef;
129              
130 24 100 100     92 if (!$specification && $schema->{openapi}) {
131 3         12 my @vers = split /\./, $schema->{openapi};
132 3         8 $specification = 'OAS' . $vers[0] . $vers[1];
133             }
134              
135 24         95 return $specification;
136             }
137              
138             1;
139              
140             __END__