File Coverage

blib/lib/OTRS/Repository/Source.pm
Criterion Covered Total %
statement 18 20 90.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 25 27 92.5


line stmt bran cond sub pod time code
1             package OTRS::Repository::Source;
2              
3 7     7   272120 use v5.10;
  7         78  
4              
5             # ABSTRACT: Parser for a single otrs.xml file
6              
7 7     7   49 use strict;
  7         18  
  7         208  
8 7     7   53 use warnings;
  7         17  
  7         228  
9              
10 7     7   1711 use Moo;
  7         51914  
  7         52  
11 7     7   12068 use HTTP::Tiny;
  7         392400  
  7         386  
12 7     7   3386 use HTTP::Tiny::FileProtocol;
  7         159950  
  7         298  
13 7     7   7317 use XML::LibXML;
  0            
  0            
14             use Regexp::Common qw(URI);
15              
16             our $VERSION = 0.06;
17              
18             our $ALLOWED_SCHEME = [ 'HTTP', 'file' ];
19              
20             has url => ( is => 'ro', required => 1, isa => \&_check_uri );
21             has content => ( is => 'ro', lazy => 1, builder => \&_get_content );
22             has tree => ( is => 'ro', lazy => 1, builder => \&_build_tree );
23             has error => ( is => 'rwp' );
24             has packages => ( is => 'rwp', default => sub { {} }, isa => sub { die "No hashref" unless ref $_[0] eq 'HASH' } );
25             has parsed => ( is => 'rwp', predicate => 1 );
26              
27             sub find {
28             my ($self, %params) = @_;
29              
30             return if !exists $params{name};
31             return if !exists $params{otrs};
32              
33             my $package = $params{name};
34             my $otrs = $params{otrs};
35              
36             if ( !defined $package || !defined $otrs ) {
37             return;
38             }
39              
40             if ( !$self->has_parsed ) {
41             $self->_parse( %params );
42             }
43              
44             my %packages = %{ $self->packages };
45              
46             return if !$packages{$package};
47             return if !$packages{$package}->{$otrs};
48              
49             my $wanted = $params{version} || $packages{$package}->{$otrs}->{latest};
50             return $packages{$package}->{$otrs}->{versions}->{$wanted};
51             }
52              
53             sub list {
54             my ($self, %params) = @_;
55              
56             if ( !$self->has_parsed ) {
57             $self->_parse( %params );
58             }
59              
60             my %packages = %{ $self->packages };
61             my $otrs = $params{otrs};
62              
63             my @packages = sort keys %packages;
64              
65             if ( $otrs ) {
66             @packages = grep{ $packages{$_}->{$otrs} }@packages;
67             }
68              
69             return @packages;
70             }
71              
72             sub _check_uri {
73             my @allowed_schemes = ref $ALLOWED_SCHEME ? @{ $ALLOWED_SCHEME } : $ALLOWED_SCHEME;
74              
75             my $matches;
76              
77             SCHEME:
78             for my $scheme ( @allowed_schemes ) {
79             my $regex = ( lc $scheme eq 'http' ) ?
80             $RE{URI}{HTTP}{-scheme => qr/https?/} :
81             $RE{URI}{$scheme};
82              
83             if ( $_[0] =~ m{\A$regex\z} ) {
84             $matches++;
85             last SCHEME;
86             }
87             }
88              
89             die "No valid URI" unless $matches;
90             return 1;
91             }
92              
93             sub _parse {
94             my ($self, %params) = @_;
95              
96             return if !$self->tree;
97              
98             my %packages = %{ $self->packages };
99              
100             my @repo_packages = $self->tree->findnodes( 'Package' );
101             my $base_url = $self->url;
102             $base_url =~ s{\w+\.xml\z}{};
103              
104             REPO_PACKAGE:
105             for my $repo_package ( @repo_packages ) {
106             my $name = $repo_package->findvalue( 'Name' );
107             my @frameworks = $repo_package->findnodes( 'Framework' );
108             my $file = $repo_package->findvalue( 'File' );
109              
110             my $version = $repo_package->findvalue( 'Version' );
111              
112             FRAMEWORK:
113             for my $framework ( @frameworks ) {
114             my $otrs_version = $framework->textContent;
115             my $short_version = join '.', (split /\./, $otrs_version, 3)[0..1];
116             my $saved_version = $packages{$name}->{$short_version}->{latest};
117              
118             if ( !$saved_version ) {
119             $packages{$name}->{$short_version} = {
120             latest => $version,
121             versions => {
122             $version => sprintf "%s%s", $base_url, $file,
123             },
124             };
125             }
126             elsif ( $self->_version_is_newer( $version, $saved_version ) ) {
127             $packages{$name}->{$short_version}->{latest} = $version;
128             $packages{$name}->{$short_version}->{versions}->{$version} =
129             sprintf "%s%s", $base_url, $file;
130             }
131             else {
132             $packages{$name}->{$short_version}->{versions}->{$version} =
133             sprintf "%s%s", $base_url, $file;
134             }
135             }
136             }
137              
138             $self->_set_parsed( 1 );
139             $self->_set_packages( \%packages );
140              
141             return 1;
142             }
143              
144             sub _version_is_newer {
145             my ($self, $new, $old) = @_;
146              
147             my @new_levels = split /\./, $new;
148             my @old_levels = split /\./, $old;
149              
150             for my $i ( 0 .. ( $#new_levels > $#old_levels ? @new_levels : @old_levels ) ) {
151             if ( !$old_levels[$i] || $new_levels[$i] > $old_levels[$i] ) {
152             return 1;
153             }
154             elsif ( $new_levels[$i] < $old_levels[$i] ) {
155             return 0;
156             }
157             }
158              
159             return 1;
160             }
161              
162             sub _get_content {
163             my $self = shift;
164             my $res = HTTP::Tiny->new->get( $self->url );
165              
166             $self->_set_error( undef );
167            
168             if ( $res->{success} ) {
169             return $res->{content};
170             }
171              
172             $self->_set_error( $res->{reason} );
173              
174             return '';
175             }
176              
177             sub _build_tree {
178             my $self = shift;
179              
180             $self->_set_error( undef );
181              
182             my $tree;
183             eval {
184             my $parser = XML::LibXML->new->parse_string( $self->content );
185             $tree = $parser->getDocumentElement;
186             } or $self->_set_error( $@ );
187              
188             return $tree;
189             }
190              
191             1;
192              
193             __END__