File Coverage

blib/lib/Bio/CIPRES.pm
Criterion Covered Total %
statement 27 29 93.1
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 37 39 94.8


line stmt bran cond sub pod time code
1             package Bio::CIPRES;
2              
3 1     1   13459 use 5.012;
  1         4  
4 1     1   4 use strict;
  1         1  
  1         17  
5 1     1   3 use warnings;
  1         4  
  1         26  
6              
7 1     1   4 use Carp;
  1         1  
  1         60  
8 1     1   498 use Config::Tiny;
  1         697  
  1         25  
9 1     1   4 use List::Util qw/first/;
  1         1  
  1         71  
10 1     1   441 use LWP;
  1         31030  
  1         31  
11 1     1   6 use URI;
  1         1  
  1         18  
12 1     1   3 use URI::Escape;
  1         1  
  1         61  
13 1     1   209 use XML::LibXML;
  0            
  0            
14              
15             use Bio::CIPRES::Job;
16             use Bio::CIPRES::Error;
17              
18             our $VERSION = '0.003001';
19             our $SERVER = 'cipresrest.sdsc.edu';
20             our $API = 'cipresrest/v1';
21             our $DOMAIN = 'Cipres Authentication';
22              
23             my %required = ( # must be defined after config parsing
24             url => "https://$SERVER/$API/",
25             timeout => 60,
26             app_id => 'cipres_perl-E9B8D52FA2A54472BF13F25E4CD957D4',
27             user => undef,
28             pass => undef,
29             );
30              
31             my %umb_only = ( # only for UMBRELLA auth
32             eu => undef, # if this is defined, then the rest are:
33             eu_email => undef, # REQUIRED
34             app_name => undef, # REQUIRED
35             eu_instition => undef, # optional
36             eu_country => undef, # optional
37             );
38              
39             my @eu_headers = qw/
40             eu
41             eu_email
42             eu_institution
43             eu_country
44             /;
45              
46             sub new {
47              
48             my ($class, %args) = @_;
49             my $self = bless {}, $class;
50              
51             # parse properties from file or constructor
52             $self->_parse_args(%args);
53              
54             # setup user agent
55             $self->{agent} = LWP::UserAgent->new(
56             agent => __PACKAGE__ . "/$VERSION",
57             ssl_opts => {verify_hostname => 0},
58             timeout => $self->{cfg}->{timeout},
59             );
60            
61             # create URI object for easier protocol/port parsing
62             $self->{uri} = URI->new( $self->{cfg}->{url} );
63              
64             my $netloc = join ':', $self->{uri}->host, $self->{uri}->port;
65             $self->{agent}->credentials(
66             $netloc,
67             $DOMAIN,
68             $self->{cfg}->{user},
69             $self->{cfg}->{pass}
70             );
71              
72             my %headers = ( 'cipres-appkey' => $self->{cfg}->{app_id} );
73              
74             $self->{account} = $self->{cfg}->{user};
75              
76             # UMBRELLA headers
77             if (defined $self->{cfg}->{eu}) {
78             croak "eu_email required for UMBRELLA authentication"
79             if (! defined $self->{cfg}->{'eu_email'});
80             croak "app_name required for UMBRELLA authentication"
81             if (! defined $self->{cfg}->{'app_name'});
82             for my $h (@eu_headers) {
83             my $val = $self->{cfg}->{$h} // next;
84             $h =~ s/_/\-/g;
85             $headers{"cipres-$h"} = $val;
86             }
87              
88             $self->{account}
89             = uri_escape( "$self->{cfg}->{app_name}.$self->{cfg}->{eu}" );
90             }
91              
92             $self->{uri}->path("/$API/job/$self->{account}");
93             $self->{agent}->default_header(%headers);
94              
95             return $self;
96              
97             }
98              
99             sub _parse_args {
100              
101             my ($self, %args) = @_;
102             my ($fn_cfg) = delete $args{conf};
103              
104             # set defaults
105             $self->{cfg} = {%required}; # copy, don't reference!
106              
107             # read from config file if asked, overwriting defaults
108             if (defined $fn_cfg) {
109             croak "Invalid or missing configuration file specified"
110             if (! -e $fn_cfg);
111             my $cfg = Config::Tiny->read( $fn_cfg )
112             or croak "Error reading configuration file: $@";
113             $self->{cfg}->{$_} = $cfg->{_}->{$_}
114             for (keys %{ $cfg->{_} });
115              
116             }
117              
118             # read parameters from constructor, overwriting if present
119             $self->{cfg}->{$_} = $args{$_} for (keys %args);
120              
121             # check that all defined fields are valid
122             my @extra = grep {! exists $required{$_} && ! exists $umb_only{$_}}
123             keys %{ $self->{cfg} };
124             croak "Unexpected config variables found (@extra) -- check syntax"
125             if (scalar @extra);
126              
127             # check that all required fields are defined
128             my @missing = grep {! defined $self->{cfg}->{$_}} keys %required;
129             croak "Required config variables missing (@missing) -- check syntax"
130             if (scalar @missing);
131              
132             # TODO: further parameter validation ???
133            
134             # Do necessary url-encoding
135             for (qw/user pass/) {
136             $self->{cfg}->{$_} = uri_escape( $self->{cfg}->{$_} );
137             }
138              
139             return 1;
140              
141             }
142              
143             sub list_jobs {
144              
145             my ($self) = @_;
146              
147             my $res = $self->_get( "$self->{uri}?expand=true" );
148             my $dom = XML::LibXML->load_xml('string' => $res);
149              
150             return map {
151             Bio::CIPRES::Job->new( agent => $self->{agent}, dom => $_ )
152             } $dom->findnodes('/joblist/jobs/jobstatus');
153              
154             }
155              
156             sub get_job {
157              
158             my ($self, $handle) = @_;
159              
160             my $res = $self->_get( "$self->{uri}/$handle" );
161             my $dom = XML::LibXML->load_xml('string' => $res);
162              
163             return Bio::CIPRES::Job->new(
164             agent => $self->{agent},
165             dom => $dom,
166             );
167              
168             }
169              
170             sub submit_job {
171              
172             my ($self, @args) = @_;
173              
174             my $res = $self->_post( $self->{uri}, @args );
175             my $dom = XML::LibXML->load_xml('string' => $res);
176              
177             return Bio::CIPRES::Job->new(
178             agent => $self->{agent},
179             dom => $dom,
180             );
181              
182             }
183              
184             sub _get {
185              
186             my ($self, $url) = @_;
187              
188             my $res = $self->{agent}->get( $url )
189             or croak "Error fetching file from $url: $@";
190              
191             die Bio::CIPRES::Error->new( $res->content )
192             if (! $res->is_success);
193              
194             return $res->content;
195              
196             }
197              
198             sub _post {
199              
200             my ($self, $url, @args) = @_;
201              
202             my $res = $self->{agent}->post(
203             $url,
204             [ @args ],
205             'content_type' => 'form-data',
206             ) or croak "Error POSTing to $url: $@";
207              
208             die Bio::CIPRES::Error->new( $res->content )
209             if (! $res->is_success);
210              
211             return $res->content;
212              
213             }
214              
215             1;
216              
217              
218             __END__