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 9     9   231910 use v5.10;
  9         74  
4              
5             # ABSTRACT: Parser for a single otrs.xml file
6              
7 9     9   47 use strict;
  9         15  
  9         182  
8 9     9   45 use warnings;
  9         14  
  9         194  
9              
10 9     9   1584 use Moo;
  9         39331  
  9         45  
11 9     9   10142 use HTTP::Tiny;
  9         349697  
  9         351  
12 9     9   2832 use HTTP::Tiny::FileProtocol;
  9         134193  
  9         319  
13 9     9   7918 use XML::LibXML;
  0            
  0            
14             use Regexp::Common qw(URI);
15              
16             our $VERSION = 0.08;
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 @package_names = sort keys %packages;
64              
65             if ( $otrs ) {
66             @package_names = grep{ $packages{$_}->{$otrs} }@package_names;
67             }
68              
69             if ( $params{details} ) {
70             my @package_list;
71              
72             NAME:
73             for my $name ( @package_names ) {
74             my @all_otrs_versions = $otrs ? $otrs : keys %{ $packages{$name} || {} };
75              
76             OTRS_VERSION:
77             for my $otrs_version ( @all_otrs_versions ) {
78              
79             VERSION:
80             for my $version ( keys %{ $packages{$name}->{$otrs_version}->{versions} || {} } ) {
81             push @package_list, {
82             name => $name,
83             version => $version,
84             url => $packages{$name}->{$otrs_version}->{versions}->{$version},
85             }
86             }
87             }
88             }
89              
90             return sort { $a->{name} cmp $b->{name} || $a->{version} cmp $b->{version} } @package_list;
91             }
92              
93             return @package_names;
94             }
95              
96             sub _check_uri {
97             my @allowed_schemes = ref $ALLOWED_SCHEME ? @{ $ALLOWED_SCHEME } : $ALLOWED_SCHEME;
98              
99             my $matches;
100              
101             SCHEME:
102             for my $scheme ( @allowed_schemes ) {
103             my $regex = ( lc $scheme eq 'http' ) ?
104             $RE{URI}{HTTP}{-scheme => qr/https?/} :
105             $RE{URI}{$scheme};
106              
107             if ( $_[0] =~ m{\A$regex\z} ) {
108             $matches++;
109             last SCHEME;
110             }
111             }
112              
113             die "No valid URI" unless $matches;
114             return 1;
115             }
116              
117             sub _parse {
118             my ($self, %params) = @_;
119              
120             return if !$self->tree;
121              
122             my %packages = %{ $self->packages };
123              
124             my @repo_packages = $self->tree->findnodes( 'Package' );
125             my $base_url = $self->url;
126             $base_url =~ s{\w+\.xml\z}{};
127              
128             REPO_PACKAGE:
129             for my $repo_package ( @repo_packages ) {
130             my $name = $repo_package->findvalue( 'Name' );
131             my @frameworks = $repo_package->findnodes( 'Framework' );
132             my $file = $repo_package->findvalue( 'File' );
133              
134             my $version = $repo_package->findvalue( 'Version' );
135              
136             FRAMEWORK:
137             for my $framework ( @frameworks ) {
138             my $otrs_version = $framework->textContent;
139             my $short_version = join '.', (split /\./, $otrs_version, 3)[0..1];
140             my $saved_version = $packages{$name}->{$short_version}->{latest};
141              
142             if ( !$saved_version ) {
143             $packages{$name}->{$short_version} = {
144             latest => $version,
145             versions => {
146             $version => sprintf "%s%s", $base_url, $file,
147             },
148             };
149             }
150             elsif ( $self->_version_is_newer( $version, $saved_version ) ) {
151             $packages{$name}->{$short_version}->{latest} = $version;
152             $packages{$name}->{$short_version}->{versions}->{$version} =
153             sprintf "%s%s", $base_url, $file;
154             }
155             else {
156             $packages{$name}->{$short_version}->{versions}->{$version} =
157             sprintf "%s%s", $base_url, $file;
158             }
159             }
160             }
161              
162             $self->_set_parsed( 1 );
163             $self->_set_packages( \%packages );
164              
165             return 1;
166             }
167              
168             sub _version_is_newer {
169             my ($self, $new, $old) = @_;
170              
171             my @new_levels = split /\./, $new;
172             my @old_levels = split /\./, $old;
173              
174             for my $i ( 0 .. ( $#new_levels > $#old_levels ? @new_levels : @old_levels ) ) {
175             if ( !$old_levels[$i] || $new_levels[$i] > $old_levels[$i] ) {
176             return 1;
177             }
178             elsif ( $new_levels[$i] < $old_levels[$i] ) {
179             return 0;
180             }
181             }
182              
183             return 1;
184             }
185              
186             sub _get_content {
187             my $self = shift;
188             my $res = HTTP::Tiny->new->get( $self->url );
189              
190             $self->_set_error( undef );
191            
192             if ( $res->{success} ) {
193             return $res->{content};
194             }
195              
196             $self->_set_error( $res->{reason} );
197              
198             return '';
199             }
200              
201             sub _build_tree {
202             my $self = shift;
203              
204             $self->_set_error( undef );
205              
206             my $tree;
207             eval {
208             my $parser = XML::LibXML->new->parse_string( $self->content );
209             $tree = $parser->getDocumentElement;
210             } or $self->_set_error( $@ );
211              
212             return $tree;
213             }
214              
215             1;
216              
217             __END__