File Coverage

blib/lib/HiveJSO.pm
Criterion Covered Total %
statement 149 150 99.3
branch 71 80 88.7
condition 17 21 80.9
subroutine 23 23 100.0
pod 3 8 37.5
total 263 282 93.2


line stmt bran cond sub pod time code
1             package HiveJSO;
2             BEGIN {
3 6     6   131693 $HiveJSO::AUTHORITY = 'cpan:GETTY';
4             }
5             # ABSTRACT: HiveJSO Perl Implementation
6             $HiveJSO::VERSION = '0.008';
7 6     6   9051 use Moo;
  6         115013  
  6         42  
8 6     6   17324 use JSON::MaybeXS;
  6         60209  
  6         357  
9 6     6   3001 use HiveJSO::Error;
  6         20  
  6         241  
10 6     6   6531 use Digest::CRC qw( crc32 );
  6         44811  
  6         620  
11 6     6   47 use Carp qw( croak );
  6         15  
  6         16286  
12              
13             our %short_attributes = qw(
14             c checksum
15             d data
16             ma manufacturer
17             maw manufacturer_web
18             maf manufacturer_factory
19             mac manufacturer_country
20             n units
21             o command
22             p product_id
23             pr product
24             prw product_web
25             prt product_timestamp
26             q supports
27             r ranges
28             s sources
29             t timestamp
30             tz timestamp_timezone
31             u unit_id
32             w software
33             x error_code
34             xt error
35             );
36              
37             our @long_attributes = qw(
38             ok
39             path
40             );
41              
42             our @attributes = sort {
43             $a cmp $b
44             } ( @long_attributes, values %short_attributes );
45              
46             has [grep { $_ ne 'unit_id' } @attributes] => (
47             is => 'ro',
48             predicate => 1,
49             );
50              
51             has unit_id => (
52             is => 'ro',
53             required => 1,
54             );
55              
56             sub new_via_json {
57 25     25 0 28095 my ( $class, $json ) = @_;
58 25         42 my %obj = %{decode_json($json)};
  25         323  
59 23         1189 return $class->new( %obj, original_json => $json );
60             }
61              
62             has original_json => (
63             is => 'ro',
64             predicate => 1,
65             );
66              
67             sub BUILDARGS {
68 26     26 0 24141 my ( $class, @args ) = @_;
69 26         40 my %orig;
70 26 100       83 if (scalar @args == 1) {
71 1         3 %orig = %{$args[0]};
  1         5  
72             } else {
73 25         82 %orig = @args;
74             }
75 26         39 my %attr;
76              
77             #
78             # only accept allowed attributes
79             # only accept short or long not mixed
80             #
81 26         32 my ( $short, $long );
82 26         51 my $short_long_error = __PACKAGE__." you can't mix short HiveJSO attribute names with long HiveJSO attributes";
83 26         70 for my $k (keys %orig) {
84 82 100       387 if (defined $short_attributes{$k}) {
  1470 100       2404  
    100          
85 12 50       29 croak $short_long_error if $long; $short = 1;
  12         15  
86 12         32 $attr{$short_attributes{$k}} = $orig{$k};
87             } elsif (grep {
88             $k eq $short_attributes{$_}
89 84         199 } keys %short_attributes) {
90 28 50       73 croak $short_long_error if $short; $long = 1;
  28         74  
91 28         97 $attr{$k} = $orig{$k};
92             } elsif (grep { $_ eq $k } @long_attributes) {
93 18         88 $attr{$k} = $orig{$k};
94             } else {
95 24 100       53 if ($k eq 'original_json') {
96 23         47 $attr{$k} = $orig{$k}; delete $orig{$k};
  23         77  
97             } else {
98 1         197 croak __PACKAGE__." '".$k."' is not a valid HiveJSO attribute";
99             }
100             }
101             }
102              
103             #
104             # remove checksum now of input attributes
105             #
106 25   100     144 my $checksum = delete $orig{c} || delete $orig{checksum};
107              
108             #
109             # we need at least 2 attributes without checksum (unit_id and one other)
110             #
111 25 100       73 if (keys %orig < 2) {
112 1         301 croak __PACKAGE__." we need more attributes for a valid HiveJSO";
113             }
114              
115             #
116             # ok must be 1
117             #
118 24 100 100     139 if (defined $attr{ok} && $attr{ok} != 1) {
119 1         208 croak __PACKAGE__." ok attribute must be set to 1 for a valid HiveJSO";
120             }
121              
122             #
123             # data attribute validation
124             #
125 23 100       118 if (defined $attr{data}) {
126 5 50       16 croak __PACKAGE__." 'data' must be an array" unless ref $attr{data} eq 'ARRAY';
127 5         6 for my $data_set (@{$attr{data}}) {
  5         11  
128 7 100       132 croak __PACKAGE__." values inside the 'data' array must be arrays" unless ref $data_set eq 'ARRAY';
129 6 100       8 croak __PACKAGE__." array inside 'data' array needs at least one value" unless scalar @{$data_set};
  6         135  
130 5 100       240 croak __PACKAGE__." first value in array inside 'data' array must be positive integer above 0" unless $data_set->[0] > 0;
131             }
132             }
133              
134             #
135             # novell check
136             #
137 20 100       1763 if (defined $attr{error_code}) {
138 1 50       208 croak __PACKAGE__." error_code must be positive integer above 0" unless $attr{error_code} > 0;
139             }
140              
141             #
142             # checksum check result is just an attribute, doesn't lead to failure
143             #
144 19 100       233 if ($checksum) {
145 6         22 my $calced_checksum = $class->calc_checksum(%orig);
146 6 100       35560 unless($calced_checksum == $checksum) {
147 1         321 croak __PACKAGE__." invalid HiveJSO checksum, should be '".$calced_checksum."'";
148             };
149             }
150 18         1627 return { %attr };
151             }
152              
153             sub calc_checksum {
154 10     10 0 47 my ( $class, %obj ) = @_;
155 22         82 my $checksum_string = join(',',map {
156 14         40 '"'.$_.'"', $class->_get_value_checksum($obj{$_})
157 22 50       133 } sort { $a cmp $b } grep {
158 10         27 $_ ne 'checksum' && $_ ne 'c'
159             } keys %obj);
160 10         53 return crc32($checksum_string);
161             }
162              
163             sub _get_value_checksum {
164 28     28   39 my ( $class, $value ) = @_;
165 28 100       65 if (ref $value eq 'ARRAY') {
166 6         17 return '['.join(',',map {
167 3         6 $class->_get_value_checksum($_)
168 3         4 } @{$value}).']';
169             }
170 25         108 return '"'.$value.'"';
171             }
172              
173             has hivejso => (
174             is => 'lazy',
175             init_arg => undef,
176             );
177              
178             sub _build_hivejso {
179 1     1   8412 my ( $self ) = @_;
180 1         6 return encode_json({
181 1         3 %{$self->hivejso_data},
182             checksum => $self->hivejso_checksum,
183             });
184             }
185              
186             has hivejso_short => (
187             is => 'lazy',
188             init_arg => undef,
189             );
190              
191             sub _build_hivejso_short {
192 1     1   4515 my ( $self ) = @_;
193 1         6 return encode_json({
194 1         4 %{$self->hivejso_data_short},
195             c => $self->hivejso_checksum_short,
196             });
197             }
198              
199             has hivejso_data => (
200             is => 'lazy',
201             init_arg => undef,
202             );
203              
204             sub _build_hivejso_data {
205 3     3   15025 my ( $self ) = @_;
206             return {
207 63 100       353 unit_id => $self->unit_id,
208             (map {
209 69 100 100     380 $self->can('has_'.$_)->($self) ? ( $_ => $self->$_ ) : ()
210             } grep {
211 3         55 $_ ne 'unit_id' && $_ ne 'checksum' && $_ ne 'c'
212             } @attributes),
213             };
214             }
215              
216             has hivejso_data_short => (
217             is => 'lazy',
218             init_arg => undef,
219             );
220              
221             sub _build_hivejso_data_short {
222 2     2   1098 my ( $self ) = @_;
223 42 100       275 my %short_data = (
224             u => $self->unit_id,
225             (map {
226 46 100 100     258 $self->can('has_'.$_)->($self) ? ( $_ => $self->$_ ) : ()
227             } grep {
228 2         15 $_ ne 'unit_id' && $_ ne 'checksum' && $_ ne 'c'
229             } @attributes),
230             );
231 2         18 for my $k (keys %short_attributes) {
232 42 50       94 if ($short_data{$short_attributes{$k}}) {
233 0         0 $short_data{$k} = delete $short_data{$short_attributes{$k}};
234             }
235             }
236 2         23 return { %short_data };
237             }
238              
239             has hivejso_checksum => (
240             is => 'lazy',
241             init_arg => undef,
242             );
243              
244             sub _build_hivejso_checksum {
245 2     2   1878 my ( $self ) = @_;
246 2         6 return $self->calc_checksum(%{$self->hivejso_data});
  2         30  
247             }
248              
249             has hivejso_checksum_short => (
250             is => 'lazy',
251             init_arg => undef,
252             );
253              
254             sub _build_hivejso_checksum_short {
255 1     1   1270 my ( $self ) = @_;
256 1         2 return $self->calc_checksum(%{$self->hivejso_data_short});
  1         25  
257             }
258              
259             sub parse {
260 11     11 1 14056 my ( $class, $string ) = @_;
261 11         237 return $class->_parse(0,$string);
262             }
263              
264             sub parse_one {
265 3     3 1 5 my ( $class, $string ) = @_;
266 3         12 return $class->_parse(1,$string);
267             }
268              
269             sub parse_seek {
270 3     3 1 14373 my ( $class, $string ) = @_;
271 3         12 my @parsed = $class->parse_one($string);
272 3         4 my ( $obj, $post );
273 3 100       10 if (ref $parsed[0]) {
    100          
274 1         2 $obj = shift @parsed;
275 1         2 $post = shift @parsed;
276             } elsif (scalar @parsed == 1) {
277 1         3 $post = shift @parsed;
278             } else {
279 1         2 shift @parsed;
280 1         2 $obj = shift @parsed;
281 1         3 $post = shift @parsed;
282             }
283 3         11 return ( $obj, $post );
284             }
285              
286             sub _parse_first {
287 15     15   23 my ( $class, $string, $current ) = @_;
288 15 100       40 my $start = defined $current
289             ? index($string,'{',$current)
290             : index($string,'{');
291 15 100       215 return if $start == -1;
292 14         24 my $end = index($string,'}',$start);
293 14 100       34 return if $end == -1;
294 10         192 my $test = substr($string,$start,$end-$start+1);
295 10         17 my $another_start = index($test,'{',1);
296 10 100       22 if ($another_start == -1) {
297 9 100       215 my @result = (
298             $start == 0 ? "" : substr($string,0,$start),
299             substr($string,$start,$end-$start+1),
300             substr($string,$end+1),
301             );
302 9         37 return @result;
303             } else {
304 1 50 33     6 return if defined $current && $another_start == $current; # TODO
305 1         7 return $class->_parse_first($string,$another_start);
306             }
307             }
308              
309             sub _parse {
310 14     14   26 my ( $class, $one, $string ) = @_;
311 14         21 my @results;
312 14         34 my @parse_first = $class->_parse_first($string);
313 14 100       51 if (@parse_first) {
314 9         193 my ( $pre, $obj, $post ) = @parse_first;
315 9 100 66     44 push @results, $pre if $pre && length($pre);
316 9         12 my $object;
317 9         13 eval {
318 9         231 $object = $class->new_via_json($obj);
319             };
320 9 100       1326 if ($@) {
321 2         41 $object = HiveJSO::Error->new(
322             garbage => $obj,
323             error => $@,
324             );
325             }
326 9         1166 push @results, $object;
327 9 100 66     66 push @results, ( $one ? $post : $class->parse($post) ) if $post && length($post);
    100          
328             } else {
329 5         10 push @results, $string;
330             }
331 14         54 return @results;
332             }
333              
334             sub add {
335 1     1 0 48 my ( $self, %newattr ) = @_;
336 1         6 my %newobj = (
337 1         3 %{$self->hivejso_data},
338             %newattr,
339             );
340 1 50       38 return $self->new(
341             %newobj,
342             $self->has_checksum ? ( checksum => $self->calc_checksum(%newobj) ) : (),
343             );
344             }
345              
346             sub add_short {
347 1     1 0 65 my ( $self, %newattr ) = @_;
348 1         7 my %newobj = (
349 1         2 %{$self->hivejso_data_short},
350             %newattr,
351             );
352 1 50       9 return $self->new(
353             %newobj,
354             $self->has_checksum ? ( c => $self->calc_checksum(%newobj) ) : (),
355             );
356             }
357              
358             1;
359              
360             __END__