File Coverage

blib/lib/LabKey/Query.pm
Criterion Covered Total %
statement 33 239 13.8
branch 0 98 0.0
condition 0 73 0.0
subroutine 11 20 55.0
pod 0 5 0.0
total 44 435 10.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             LabKey::Query
6              
7             =head1 SYNOPSIS
8              
9             use LabKey::Query;
10             my $results = LabKey::Query::selectRows(
11             -baseUrl => 'http://labkey.com:8080/labkey/',
12             -containerPath => 'myFolder/',
13             -schemaName => 'lists',
14             -queryName => 'mid_tags',
15             );
16            
17             =head1 ABSTRACT
18              
19             For interacting with data in LabKey Server
20              
21             =head1 DESCRIPTION
22              
23             This module is designed to simplify querying and manipulating data in LabKey Server. It should more or less replicate the javascript APIs of the same names.
24              
25             After the module is installed, if you need to login with a specific user you
26             will need to create a .netrc file in the home directory of the user
27             running the perl script. Documentation on .netrc can be found here:
28             https://www.labkey.org/wiki/home/Documentation/page.view?name=netrc
29              
30             In API versions 0.08 and later, you can specify the param '-loginAsGuest'
31             which will query the server without any credentials. The server must permit
32             guest to that folder for this to work though.
33              
34             =head1 SEE ALSO
35              
36             The LabKey client APIs are described in greater detail here:
37             https://www.labkey.org/wiki/home/Documentation/page.view?name=viewAPIs
38              
39             Support questions should be directed to the LabKey forum:
40             https://www.labkey.org/announcements/home/Server/Forum/list.view?
41              
42             =head1 AUTHOR
43              
44             LabKey Software C
45              
46             =head1 CONTRIBUTING
47              
48             Send comments, suggestions and bug reports to:
49              
50             L
51              
52              
53             =head1 COPYRIGHT
54            
55             Copyright (c) 2010 Ben Bimber
56             Copyright (c) 2011-2013 LabKey Software
57              
58             Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
59              
60             =cut
61              
62             package LabKey::Query;
63              
64 1     1   20235 use strict;
  1         4  
  1         38  
65 1     1   1064 use LWP::UserAgent;
  1         48348  
  1         25  
66 1     1   8 use HTTP::Request;
  1         5  
  1         17  
67 1     1   1033 use JSON;
  1         12261  
  1         6  
68 1     1   1085 use Data::Dumper;
  1         6265  
  1         56  
69 1     1   643 use FileHandle;
  1         10524  
  1         5  
70 1     1   316 use File::Spec;
  1         2  
  1         22  
71 1     1   831 use File::HomeDir;
  1         5578  
  1         49  
72 1     1   5 use Carp;
  1         2  
  1         42  
73 1     1   4 use URI;
  1         1  
  1         28  
74              
75 1     1   4 use vars qw($VERSION);
  1         2  
  1         2658  
76              
77             our $VERSION = "1.04";
78              
79              
80              
81             =head1 selectRows()
82              
83             selectRows() can be used to query data from LabKey server
84              
85             The following are the minimum required params:
86            
87             my $results = LabKey::Query::selectRows(
88             -baseUrl => 'http://labkey.com:8080/labkey/',
89             -containerPath => 'myFolder/',
90             -schemaName => 'lists',
91             -queryName => 'mid_tags',
92             );
93              
94             The following are optional:
95              
96             -viewName => 'view1',
97             -filterArray => [
98             ['file_active', 'eq', 1],
99             ['species', 'neq', 'zebra']
100             ], #allows filters to be applied to the query similar to the labkey Javascript API.
101             -parameters => [
102             ['enddate', '2011/01/01'],
103             ['totalDays', 15]
104             ], #allows parameters to be applied to the query similar to the labkey Javascript API.
105             -maxRows => 10 #the max number of rows returned
106             -sort => 'ColumnA,ColumnB' #sort order used for this query
107             -offset => 100 #the offset used when running the query
108             -columns => 'ColumnA,ColumnB' #A comma-delimited list of column names to include in the results.
109             -containerFilterName => 'currentAndSubfolders'
110             -debug => 1, #will result in a more verbose output
111             -loginAsGuest => #will not attempt to lookup credentials in netrc
112             -netrcFile => optional. the location of a file to use in place of a .netrc file. see also the environment variable LABKEY_NETRC.
113             -requiredVersion => 9.1 #if 8.3 is selected, it will use LabKey's pre-9.1 format for returning the data. 9.1 is the default. See documentation of LABKEY.Query.ExtendedSelectRowsResults for more detail here:
114             https://www.labkey.org/download/clientapi_docs/javascript-api/symbols/LABKEY.Query.html
115             -useragent => an instance of LWP::UserAgent (if not provided, a new instance will be created)
116             -timeout => timeout in seconds (used when creating a new LWP::UserAgent)
117            
118             NOTE:
119              
120             - In version 1.0 and later of the perl API, the default result format is 9.1. This is different from the LabKey JS, which defaults to the earlier format for legacy purposes.
121             - The environment variable 'LABKEY_URL' can be used instead of supplying a '-baseUrl' param
122             - The environment variable 'LABKEY_NETRC' can be used to specify an alternate location of a netrc file, if not in the user's home directory.
123              
124             =cut
125              
126             sub selectRows {
127              
128 0     0 0   my %args = @_;
129            
130             #allow baseUrl as environment variable
131 0   0       $args{'-baseUrl'} = $args{'-baseUrl'} || $ENV{LABKEY_URL};
132            
133             #sanity checking
134 0           my @required = ( '-containerPath', '-queryName', '-schemaName', '-baseUrl' );
135 0           foreach (@required) {
136 0 0         if ( !$args{$_} ) { croak("ERROR: Missing required param: $_") }
  0            
137             }
138              
139 0           my $url = URI->new(
140             _normalizeSlash($args{'-baseUrl'})
141             . "query/"
142             . _normalizeSlash($args{'-containerPath'})
143             . "getQuery.api?"
144             );
145            
146             #if no machine supplied, extract domain from baseUrl
147 0 0         if (!$args{'-machine'}){
148 0           $args{'-machine'} = $url->host;
149             }
150              
151 0           my $lk_config;
152 0   0       my $netrc_file = $args{-netrcFile} || $ENV{LABKEY_NETRC};
153 0 0         if(!$args{'-loginAsGuest'}){
154 0           $lk_config = _readrc( $args{-machine}, $netrc_file );
155             }
156            
157 0   0       my %params = (
158             schemaName => $args{'-schemaName'},
159             "query.queryName" => $args{'-queryName'},
160             apiVersion => $args{'-requiredVersion'} || 9.1,
161             );
162              
163 0           foreach ( @{ $args{-filterArray} } ) {
  0            
164 0           $params{"query." . @{$_}[0] . "~" . @{$_}[1]} = @{$_}[2] ;
  0            
  0            
  0            
165             }
166              
167 0           foreach ( @{ $args{-parameters} } ) {
  0            
168 0           $params{"query.param." . @{$_}[0]} = @{$_}[1];
  0            
  0            
169             }
170            
171 0           foreach ('viewName', 'offset', 'sort', 'maxRows', 'columns', 'containerFilterName'){
172 0 0         if ( $args{'-'.$_} ) {
173 0           $params{"query.".$_} = $args{'-'.$_};
174             }
175             }
176            
177 0           $url->query_form(%params);
178            
179 0 0         print $url."\n" if $args{-debug};
180              
181             #Fetch the actual data from the query
182 0           my $request = HTTP::Request->new( "GET" => $url );
183 0 0         if($lk_config){
184 0           $request->authorization_basic( $$lk_config{'login'}, $$lk_config{'password'} );
185             }
186 0   0       my $ua = $args{'-useragent'} || _createUserAgent( %args );
187 0           my $response = $ua->request($request);
188              
189             # Simple error checking
190 0 0         if ( $response->is_error ) {
191 0           croak( $response->status_line );
192             }
193              
194 0   0       my $json_obj = JSON->new->utf8->decode( $response->content )
195             || croak("ERROR: Unable to decode JSON.\n$url\n");
196              
197 0           return $json_obj;
198              
199             }
200              
201              
202             =head1 insertRows()
203              
204             insertRows() can be used to insert records into a LabKey table
205              
206             The following are the minimum required params:
207              
208             my $insert = LabKey::Query::insertRows(
209             -baseUrl => 'http://labkey.com:8080/labkey/',
210             -containerPath => 'myFolder/',
211             -schemaName => 'lists',
212             -queryName => 'backup',
213             -rows => [{
214             "JobName" => 'jobName',
215             "Status" => $status,
216             "Log" => $log,
217             "Date" => $date
218             }],
219             );
220            
221             The following are optional:
222              
223             -debug => 1, #will result in a more verbose output
224             -loginAsGuest => #will not attempt to lookup credentials in netrc
225             -netrcFile => optional. the location of a file to use in place of a .netrc file. see also the environment variable LABKEY_NETRC.
226             -useragent => an instance of LWP::UserAgent (if not provided, a new instance will be created)
227             -timeout => timeout in seconds (used when creating a new LWP::UserAgent)
228              
229             NOTE:
230             - The environment variable 'LABKEY_URL' can be used instead of supplying a '-baseUrl' param
231             - The environment variable 'LABKEY_NETRC' can be used to specify an alternate location of a netrc file, if not in the user's home directory
232              
233             =cut
234              
235             sub insertRows {
236 0     0 0   my %args = @_;
237              
238             #allow baseUrl as an environment variable
239 0   0       $args{'-baseUrl'} = $args{'-baseUrl'} || $ENV{LABKEY_URL};
240              
241             #sanity checking
242 0           my @required = ( '-containerPath', '-queryName', '-schemaName', '-baseUrl', '-rows' );
243 0           foreach (@required) {
244 0 0         if ( !$args{$_} ) { croak("ERROR: Missing required param: $_") }
  0            
245             }
246              
247             #if no machine supplied, extract domain from baseUrl
248 0 0         if (!$args{'-machine'}){
249 0           my $url = URI->new($args{'-baseUrl'});
250 0           $args{'-machine'} = $url->host;
251             }
252            
253 0           my $lk_config;
254 0   0       my $netrc_file = $args{-netrcFile} || $ENV{LABKEY_NETRC};
255 0 0         if(!$args{'-loginAsGuest'}){
256 0           $lk_config = _readrc( $args{-machine}, $netrc_file );
257             }
258              
259 0           my $url =
260             _normalizeSlash($args{'-baseUrl'})
261             . "query/"
262             . _normalizeSlash($args{'-containerPath'})
263             . "insertRows.api";
264              
265 0 0         print $url."\n" if $args{-debug};
266              
267 0           my $data = {
268             "schemaName" => $args{'-schemaName'},
269             "queryName" => $args{'-queryName'},
270             "rows" => $args{'-rows'}
271             };
272              
273 0   0       my $ua = $args{'-useragent'} || _createUserAgent( %args );
274 0           my $response = _postData($ua, $url, $data, $lk_config);
275 0           return $response;
276              
277             }
278              
279              
280             =head1 updateRows()
281              
282             updateRows() can be used to update records in a LabKey table
283              
284             The following are the minimum required params:
285              
286             my $update = LabKey::Query::updateRows(
287             -baseUrl => 'http://labkey.com:8080/labkey/',
288             -containerPath => 'myFolder/',
289             -schemaName => 'lists',
290             -queryName => 'backup',
291             -rows => [{
292             "JobName" => 'jobName',
293             "Status" => $status,
294             "Log" => $log,
295             "Date" => $date
296             }],
297             );
298            
299             The following are optional:
300              
301             -debug => 1, #will result in a more verbose output
302             -loginAsGuest => #will not attempt to lookup credentials in netrc
303             -netrcFile => optional. the location of a file to use in place of a .netrc file. see also the environment variable LABKEY_NETRC.
304             -useragent => an instance of LWP::UserAgent (if not provided, a new instance will be created)
305             -timeout => timeout in seconds (used when creating a new LWP::UserAgent)
306              
307             NOTE:
308             - The environment variable 'LABKEY_URL' can be used instead of supplying a '-baseUrl' param
309             - The environment variable 'LABKEY_NETRC' can be used to specify an alternate location of a netrc file, if not in the user's home directory
310            
311             =cut
312              
313             sub updateRows {
314 0     0 0   my %args = @_;
315              
316             #allow baseUrl as environment variable
317 0   0       $args{'-baseUrl'} = $args{'-baseUrl'} || $ENV{LABKEY_URL};
318              
319             #sanity checking
320 0           my @required = ( '-containerPath', '-queryName', '-schemaName', '-baseUrl', '-rows' );
321 0           foreach (@required) {
322 0 0         if ( !$args{$_} ) { croak("ERROR: Missing required param: $_") }
  0            
323             }
324              
325             #if no machine supplied, extract domain from baseUrl
326 0 0         if (!$args{'-machine'}){
327 0           my $url = URI->new($args{'-baseUrl'});
328 0           $args{'-machine'} = $url->host;
329             }
330 0           my $lk_config;
331 0   0       my $netrc_file = $args{-netrcFile} || $ENV{LABKEY_NETRC};
332 0 0         if(!$args{'-loginAsGuest'}){
333 0           $lk_config = _readrc( $args{-machine}, $netrc_file );
334             }
335              
336 0           my $url =
337             _normalizeSlash($args{'-baseUrl'})
338             . "query/"
339             . _normalizeSlash($args{'-containerPath'})
340             . "updateRows.api";
341              
342 0 0         print $url."\n" if $args{-debug};
343              
344 0           my $data = {
345             "schemaName" => $args{'-schemaName'},
346             "queryName" => $args{'-queryName'},
347             "rows" => $args{'-rows'}
348             };
349              
350 0   0       my $ua = $args{'-useragent'} || _createUserAgent( %args );
351 0           my $response = _postData($ua, $url, $data, $lk_config);
352 0           return $response;
353              
354             }
355              
356              
357             =head1 deleteRows()
358              
359             deleteRows() can be used to delete records in a LabKey table
360              
361             The following are the minimum required params:
362              
363             my $update = LabKey::Query::deleteRows(
364             -baseUrl => 'http://labkey.com:8080/labkey/',
365             -containerPath => 'myFolder/',
366             -schemaName => 'lists',
367             -queryName => 'backup',
368             -rows => [{
369             "Key" => '12',
370             }],
371             );
372            
373             The following are optional:
374              
375             -debug => 1, #will result in a more verbose output
376             -loginAsGuest => #will not attempt to lookup credentials in netrc
377             -netrcFile => optional. the location of a file to use in place of a .netrc file. see also the environment variable LABKEY_NETRC.
378             -useragent => an instance of LWP::UserAgent (if not provided, a new instance will be created)
379             -timeout => timeout in seconds (used when creating a new LWP::UserAgent)
380              
381             NOTE:
382             - The environment variable 'LABKEY_URL' can be used instead of supplying a '-baseUrl' param
383             - The environment variable 'LABKEY_NETRC' can be used to specify an alternate location of a netrc file, if not in the user's home directory
384            
385             =cut
386              
387             sub deleteRows {
388 0     0 0   my %args = @_;
389              
390             #allow baseUrl as environment variable
391 0   0       $args{'-baseUrl'} = $args{'-baseUrl'} || $ENV{LABKEY_URL};
392              
393             #sanity checking
394 0           my @required = ( '-containerPath', '-queryName', '-schemaName', '-baseUrl', '-rows' );
395 0           foreach (@required) {
396 0 0         if ( !$args{$_} ) { croak("ERROR: Missing required param: $_") }
  0            
397             }
398              
399             #if no machine supplied, extract domain from baseUrl
400 0 0         if (!$args{'-machine'}){
401 0           my $url = URI->new($args{'-baseUrl'});
402 0           $args{'-machine'} = $url->host;
403             }
404 0           my $lk_config;
405 0   0       my $netrc_file = $args{-netrcFile} || $ENV{LABKEY_NETRC};
406 0 0         if(!$args{'-loginAsGuest'}){
407 0           $lk_config = _readrc( $args{-machine}, $netrc_file );
408             }
409              
410 0           my $url =
411             _normalizeSlash($args{'-baseUrl'})
412             . "query/"
413             . _normalizeSlash($args{'-containerPath'})
414             . "deleteRows.api";
415              
416 0 0         print $url."\n" if $args{-debug};
417              
418 0           my $data = {
419             "schemaName" => $args{'-schemaName'},
420             "queryName" => $args{'-queryName'},
421             "rows" => $args{'-rows'}
422             };
423              
424 0   0       my $ua = $args{'-useragent'} || _createUserAgent( %args );
425 0           my $response = _postData($ua, $url, $data, $lk_config);
426 0           return $response;
427              
428             }
429              
430              
431             =head1 executeSql()
432              
433             executeSql() can be used to execute arbitrary SQL
434              
435             The following are the minimum required params:
436              
437             my $result = LabKey::Query::executeSql(
438             -baseUrl => 'http://labkey.com:8080/labkey/',
439             -containerPath => 'myFolder/',
440             -schemaName => 'study',
441             -sql => 'select MyDataset.foo, MyDataset.bar from MyDataset',
442             );
443            
444             The following are optional:
445              
446             -maxRows => 10 #the max number of rows returned
447             -sort => 'ColumnA,ColumnB' #sort order used for this query
448             -offset => 100 #the offset used when running the query
449             -containerFilterName => 'currentAndSubfolders'
450             -debug => 1, #will result in a more verbose output
451             -loginAsGuest => #will not attempt to lookup credentials in netrc
452             -netrcFile => optional. the location of a file to use in place of a .netrc file. see also the environment variable LABKEY_NETRC.
453             -useragent => an instance of LWP::UserAgent (if not provided, a new instance will be created)
454             -timeout => timeout in seconds (used when creating a new LWP::UserAgent)
455              
456             NOTE:
457             - The environment variable 'LABKEY_URL' can be used instead of supplying a '-baseUrl' param
458             - The environment variable 'LABKEY_NETRC' can be used to specify an alternate location of a netrc file, if not in the user's home directory
459            
460             =cut
461              
462             sub executeSql {
463 0     0 0   my %args = @_;
464              
465             #allow baseUrl as environment variable
466 0   0       $args{'-baseUrl'} = $args{'-baseUrl'} || $ENV{LABKEY_URL};
467              
468             #sanity checking
469 0           my @required = ( '-containerPath', '-baseUrl', '-sql' );
470 0           foreach (@required) {
471 0 0         if ( !$args{$_} ) { croak("ERROR: Missing required param: $_") }
  0            
472             }
473              
474             #if no machine supplied, extract domain from baseUrl
475 0 0         if (!$args{'-machine'}){
476 0           my $url = URI->new($args{'-baseUrl'});
477 0           $args{'-machine'} = $url->host;
478             }
479 0           my $lk_config;
480 0   0       my $netrc_file = $args{-netrcFile} || $ENV{LABKEY_NETRC};
481 0 0         if(!$args{'-loginAsGuest'}){
482 0           $lk_config = _readrc( $args{-machine}, $netrc_file );
483             }
484              
485 0           my $url =
486             _normalizeSlash($args{'-baseUrl'})
487             . "query/"
488             . _normalizeSlash($args{'-containerPath'})
489             . "executeSql.api?";
490              
491 0 0         print $url."\n" if $args{-debug};
492            
493 0           my $data = {
494             "schemaName" => $args{'-schemaName'},
495             "sql" => $args{'-sql'},
496             };
497            
498 0           foreach ('offset', 'sort', 'maxRows', 'containerFilterName'){
499 0 0         if ( $args{'-'.$_} ) {
500 0           $$data{$_} = $args{'-'.$_};
501             }
502             }
503            
504 0 0         print Dumper($data) if $args{-debug};
505            
506 0   0       my $ua = $args{'-useragent'} || _createUserAgent( %args );
507 0           my $response = _postData($ua, $url, $data, $lk_config);
508 0           return $response;
509              
510             }
511              
512              
513              
514              
515             # NOTE: this code adapted from Net::Netrc module. It was changed so alternate locations could be supplied for a .netrc file
516             sub _readrc() {
517              
518 0   0 0     my $host = shift || 'default';
519 0           my $file = shift;
520              
521             #allow user to supply netrc location
522 0 0 0       if(!$file || !-e $file){
523 0           $file = File::Spec->catfile( File::HomeDir::home(), '.netrc' );
524             }
525 0 0         if ( !-e $file ) {
526 0           $file = File::Spec->catfile( File::HomeDir::home(), '_netrc' );
527             }
528              
529 0           my %netrc = ();
530 0           my ( $login, $pass, $acct ) = ( undef, undef, undef );
531 0           my $fh;
532 0           local $_;
533              
534 0           $netrc{default} = undef;
535              
536             # OS/2 and Win32 do not handle stat in a way compatable with this check :-(
537 0 0 0       unless ( $^O eq 'os2'
      0        
      0        
538             || $^O eq 'MSWin32'
539             || $^O eq 'MacOS'
540             || $^O =~ /^cygwin/ )
541             {
542 0           my @stat = stat($file);
543              
544 0 0         if (@stat) {
545 0 0         if ( $stat[2] & 077 ) {
546 0           carp "Bad permissions: $file";
547 0           return;
548             }
549 0 0         if ( $stat[4] != $< ) {
550 0           carp "Not owner: $file";
551 0           return;
552             }
553             }
554             }
555              
556 0 0         if ( $fh = FileHandle->new( $file, "r" ) ) {
557 0           my ( $mach, $macdef, $tok, @tok ) = ( 0, 0 );
558              
559 0           while (<$fh>) {
560 0 0         undef $macdef if /\A\n\Z/;
561              
562 0 0         if ($macdef) {
563 0           push( @$macdef, $_ );
564 0           next;
565             }
566              
567 0           s/^\s*//;
568 0           chomp;
569              
570 0   0       while ( length && s/^("((?:[^"]+|\\.)*)"|((?:[^\\\s]+|\\.)*))\s*// )
571             {
572 0           ( my $tok = $+ ) =~ s/\\(.)/$1/g;
573 0           push( @tok, $tok );
574             }
575              
576             TOKEN:
577 0           while (@tok) {
578 0 0         if ( $tok[0] eq "default" ) {
579 0           shift(@tok);
580 0           $mach = bless {};
581 0           $netrc{default} = [$mach];
582              
583 0           next TOKEN;
584             }
585              
586             last TOKEN
587 0 0         unless @tok > 1;
588              
589 0           $tok = shift(@tok);
590              
591 0 0         if ( $tok eq "machine" ) {
    0          
    0          
592 0           my $host = shift @tok;
593 0           $mach = { machine => $host };
594              
595 0 0         $netrc{$host} = []
596             unless exists( $netrc{$host} );
597 0           push( @{ $netrc{$host} }, $mach );
  0            
598             }
599             elsif ( $tok =~ /^(login|password|account)$/ ) {
600 0 0         next TOKEN unless $mach;
601 0           my $value = shift @tok;
602              
603             # Following line added by rmerrell to remove '/' escape char in .netrc
604 0           $value =~ s/\/\\/\\/g;
605 0           $mach->{$1} = $value;
606             }
607             elsif ( $tok eq "macdef" ) {
608 0 0         next TOKEN unless $mach;
609 0           my $value = shift @tok;
610 0 0         $mach->{macdef} = {}
611             unless exists $mach->{macdef};
612 0           $macdef = $mach->{machdef}{$value} = [];
613             }
614             }
615             }
616 0           $fh->close();
617             }
618            
619 0           my $auth = $netrc{$host}[0];
620              
621             #if no machine is specified and there is only 1 machine in netrc, we use that one
622 0 0 0       if (!$auth && length((keys %netrc))==1){
623 0           $auth = $netrc{(keys %netrc)[0]}[0];
624             }
625              
626 0 0         warn("Unable to find entry for host: $host") unless $auth;
627 0 0         warn("Missing password for host: $host") unless $auth->{password};
628 0 0         warn("Missing login for host: $host") unless $auth->{login};
629              
630 0           return $auth;
631             }
632              
633              
634             sub _normalizeSlash(){
635 0     0     my $containerPath = shift;
636            
637 0           $containerPath =~ s/^\///;
638 0           $containerPath =~ s/\/$//;
639 0           $containerPath .= '/';
640 0           return $containerPath;
641             }
642              
643              
644             sub _postData(){
645 0     0     my ($ua, $url, $data, $lk_config) = @_;
646            
647 0           my $json_obj = JSON->new->utf8->encode($data);
648              
649 0           my $req = new HTTP::Request;
650 0           $req->method('POST');
651 0           $req->url($url);
652 0           $req->content_type('application/json');
653 0           $req->content($json_obj);
654 0           $req->authorization_basic( $$lk_config{'login'}, $$lk_config{'password'} );
655 0           my $response = $ua->request($req);
656              
657             # Simple error checking
658 0 0         if ( $response->is_error ) {
659 0           croak($response->status_line);
660             }
661              
662             #print Dumper($response);
663 0   0       $json_obj = JSON->new->utf8->decode( $response->content )
664             || croak("ERROR: Unable to decode JSON.\n$url\n");
665            
666 0           return $json_obj;
667             }
668              
669             sub _createUserAgent() {
670 0     0     my %args = @_;
671              
672 0           my $ua = new LWP::UserAgent;
673 0           $ua->agent("Perl API Client/1.0");
674 0 0         if ($args{'-timeout'}) {
675 0           print "setting timeout to " . $args{'-timeout'} . "\n";
676 0           $ua->timeout($args{'-timeout'});
677             }
678 0           return $ua;
679             }
680              
681              
682             1;
683