File Coverage

blib/lib/HTTP/API/Client.pm
Criterion Covered Total %
statement 246 311 79.1
branch 80 130 61.5
condition 13 33 39.3
subroutine 46 55 83.6
pod 0 17 0.0
total 385 546 70.5


line stmt bran cond sub pod time code
1             package HTTP::API::Client;
2             $HTTP::API::Client::VERSION = '1.04';
3 7     7   450410 use Moo;
  7         87619  
  7         43  
4              
5             =head1 NAME
6              
7             HTTP::API::Client - API Client
8              
9             =head1 USAGE
10              
11             use HTTP::API::Client;
12              
13             my $ua1 = HTTP::API::Client->new;
14             my $ua2 = HTTP::API::Client->new(base_url => URI->new( $url ), pre_defined_headers => { X_COMPANY => 'ABC LTD' } );
15             my $ua3 = HTTP::API::Client->new(base_url => URI->new( $url ), pre_defined_data => { api_key => 123 } );
16              
17             $ua->send( $method, $url, \%data, \%header );
18              
19             Send short hand methods - get, post, head, put and delete
20              
21             Example:
22              
23             $ua->get( $url ) same as $ua->send( GET, $url );
24             $ua->post( $url, \%data, \%headers ) same as $ua->send( GET, $url, \%data, \%headers );
25              
26             Get Json Data - grab the content body from the response and json decode
27              
28             $ua = HTTP::API::Client->new(base_url => URI->new("http://google.com"));
29             $ua->get("/search" => { q => "something" });
30             my $hashref_from_decoded_json_string = $ua->json_response;
31             ## ps. this is just an example to get json from a rest api
32              
33             Send a query string to server
34              
35             $ua = HTTP::API::Client->new( content_type => "application/x-www-form-urlencoded" );
36             $ua->post("http://google.com", { q => "something" });
37             my $response = $ua->last_response; ## is a HTTP::Response object
38              
39             At the moment, only support query string and json data in and out
40              
41             =head1 ENVIRONMENT VARIABLES
42              
43             These enviornment variables expose the controls without changing the existing code.
44              
45             HTTP VARIABLES
46              
47             HTTP_USERNAME - basic auth username
48             HTTP_PASSWORD - basic auth password
49             HTTP_AUTH_TOKEN - basic auth token string
50             HTTP_CHARSET - content type charset. default utf8
51             HTTP_TIMEOUT - timeout the request for ??? seconds. default 60 seconds.
52             SSL_VERIFY - verify ssl url. default is off
53              
54             DEBUG VARIABLES
55              
56             DEBUG_IN_OUT - print out request and response in string to STDERR
57             DEBUG_SEND_OUT - print out request in string to STDERR
58             DEBUG_RESPONSE - print out response in string to STDERR
59             DEBUG_RESPONSE_HEADER_ONLY - print out response header only without the body
60             DEBUG_RESPONSE_IF_FAIL - only print out response in string if fail.
61              
62             RETRY VARIABLES
63              
64             RETRY_FAIL_RESPONSE - number of time to retry if resposne comes back is failed. default 0 retry
65             RETRY_FAIL_STATUS - only retry if specified status code. e.g. 500,404
66             RETRY_DELAY - retry with wait time of ??? seconds in between
67              
68             =cut
69              
70 7     7   15225 use Encode;
  7         118551  
  7         762  
71 7     7   4179 use HTTP::Headers;
  7         30871  
  7         337  
72 7     7   4159 use HTTP::Request;
  7         109638  
  7         280  
73 7     7   6171 use JSON::XS;
  7         38459  
  7         528  
74 7     7   5271 use LWP::UserAgent;
  7         196822  
  7         349  
75 7     7   89 use Try::Tiny;
  7         16  
  7         529  
76 7     7   46 use URI;
  7         18  
  7         280  
77 7     7   42 use URI::Escape qw( uri_escape uri_unescape );
  7         20  
  7         450  
78 7     7   50 use Scalar::Util qw( looks_like_number );
  7         16  
  7         318  
79 7     7   4491 use HTTP::API::DataTypeMarker;
  7         50  
  7         30547  
80              
81             extends 'Exporter';
82              
83             our @EXPORT = qw( xCSV xBOOLEAN
84             xTRUE xFALSE
85             xTrue xFalse
86             xtrue xfalse
87             xt__e xf___e
88             );
89              
90             has username => (
91             is => "rw",
92             lazy => 1,
93             builder => 1,
94             );
95              
96 5     5   75 sub _build_username { _defor($ENV{HTTP_USERNAME}, '') }
97              
98             has password => (
99             is => "rw",
100             lazy => 1,
101             builder => 1,
102             );
103              
104 5     5   60 sub _build_password { _defor($ENV{HTTP_PASSWORD}, '') }
105              
106             has auth_token => (
107             is => "rw",
108             lazy => 1,
109             builder => 1,
110             );
111              
112 6     6   73 sub _build_auth_token { _defor($ENV{HTTP_AUTH_TOKEN}, '') }
113              
114             has base_url => (
115             is => "rw",
116             lazy => 1,
117             builder => 1,
118             );
119              
120       4     sub _build_base_url {}
121              
122             has last_response => (
123             is => "rw",
124             lazy => 1,
125             builder => 1,
126             );
127              
128       0     sub _build_last_response {}
129              
130             has charset => (
131             is => "rw",
132             lazy => 1,
133             builder => 1,
134             );
135              
136 5     5   66 sub _build_charset { _defor($ENV{HTTP_CHARSET}, "utf8") }
137              
138             has browser_id => (
139             is => "rw",
140             lazy => 1,
141             builder => 1,
142             );
143              
144             sub _build_browser_id {
145 6     6   104 my $ver = _defor($HTTP::API::Client::VERSION, -1);
146 6         56 return "HTTP API Client v$ver";
147             }
148              
149             has content_type => (
150             is => "rw",
151             lazy => 1,
152             builder => 1,
153             );
154              
155       3     sub _build_content_type {}
156              
157             sub get_content_type {
158 30     30 0 123 my ($self, %o) = @_;
159 30         622 my $content_type = $self->content_type;
160              
161 30 100       227 if ($content_type) {
162 21         102 return $content_type;
163             }
164              
165 9         18 my $method = ${$o{method}};
  9         22  
166              
167 9 50       32 if ($method eq 'GET') {
168 9         68 return 'application/x-www-form-urlencoded';
169             }
170              
171 0         0 my $charset = $self->charset;
172 0         0 return "application/json; charset=$charset";
173             }
174              
175             has engine => (
176             is => "ro",
177             lazy => 1,
178             builder => 1,
179             );
180              
181 6     6   70 sub _build_engine {"LWP::UserAgent"}
182              
183             has ua => (
184             is => "rw",
185             lazy => 1,
186             builder => 1,
187             );
188              
189             sub _build_ua {
190 6     6   64 my ($self) = @_;
191 6         124 my $ssl_verify = $self->ssl_verify;
192 6         117 my $engine = $self->engine;
193              
194 6         15 my $ua;
195              
196 6 50       24 if ( $engine eq "LWP::UserAgent" ) {
197 6         69 $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => $ssl_verify } );
198 6         17104 $ua->agent( $self->browser_id );
199 6         771 $ua->timeout( $self->timeout );
200             }
201             else {
202 0         0 $ua = $self->$engine($ssl_verify);
203             }
204              
205 6         164 return $ua;
206             }
207              
208             has ssl_verify => (
209             is => "rw",
210             lazy => 1,
211             builder => 1,
212             );
213              
214             sub _build_ssl_verify {
215 6     6   71 return _defor( $ENV{SSL_VERIFY}, 0 );
216             }
217              
218             has retry => (
219             is => "rw",
220             lazy => 1,
221             builder => 1,
222             );
223              
224             sub _build_retry {
225 6     6   69 my ($self) = @_;
226 6         14 my %retry = %{ _defor($self->retry_config, {}) };
  6         113  
227 6         22 my $count = $retry{fail_response};
228 6         39 my %status = map { $_ => 1 } split /,/, $retry{fail_status};
  0         0  
229              
230 6         16 my $delay = $retry{delay};
231              
232             return {
233 6         52 count => $count,
234             status => \%status,
235             delay => $delay,
236             };
237             }
238              
239             has retry_config => (
240             is => "rw",
241             lazy => 1,
242             builder => 1,
243             );
244              
245             sub _build_retry_config {
246             return {
247             fail_response => _defor( $ENV{RETRY_FAIL_RESPONSE}, 0 ),
248             fail_status => _defor($ENV{RETRY_FAIL_STATUS}, ''),
249 6     6   79 delay => _defor( $ENV{RETRY_DELAY}, 5 ),
250             };
251             }
252              
253             has timeout => (
254             is => "rw",
255             lazy => 1,
256             builder => 1,
257             );
258              
259 6     6   89 sub _build_timeout { _defor($ENV{HTTP_TIMEOUT}, 60) }
260              
261             has json => (
262             is => "rw",
263             lazy => 1,
264             builder => 1,
265             );
266              
267             sub _build_json {
268 2     2   24 my ($self) = @_;
269 2         39 my $json = JSON::XS->new->canonical->allow_nonref;
270 2         42 my $charset = $self->charset;
271 2         5 eval { $json->$charset };
  2         12  
272 2         62 return $json;
273             }
274              
275             has debug_flags => (
276             is => "rw",
277             lazy => 1,
278             builder => 1,
279             );
280              
281             sub _build_debug_flags {
282             return {
283             in_out => $ENV{DEBUG_IN_OUT},
284             send_out => $ENV{DEBUG_SEND_OUT},
285             response => $ENV{DEBUG_RESPONSE},
286             response_header_only => $ENV{DEBUG_RESPONSE_HEADER_ONLY},
287             response_if_fail => $ENV{DEBUG_RESPONSE_IF_FAIL},
288 6     6   109 };
289             }
290              
291             has pre_defined_data => (
292             is => "rw",
293             lazy => 1,
294             builder => 1,
295             );
296              
297 4     4   52 sub _build_pre_defined_data {{}}
298              
299             has pre_defined_headers => (
300             is => "rw",
301             lazy => 1,
302             builder => 1,
303             );
304              
305 6     6   76 sub _build_pre_defined_headers {{}}
306              
307             has pre_defined_events => (
308             is => "rw",
309             lazy => 1,
310             builder => 1,
311             );
312              
313 6     6   84 sub _build_pre_defined_events {{}}
314              
315             sub get {
316 6     6 0 15916 my ($self, @args) = @_;
317 6         38 return $self->send( GET => @args );
318             }
319              
320             sub post {
321 5     5 0 9120 my ($self, @args) = @_;
322 5         24 return $self->send( POST => @args );
323             }
324              
325             sub put {
326 0     0 0 0 my ($self, @args) = @_;
327 0         0 return $self->send( PUT => @args );
328             }
329              
330             sub head {
331 0     0 0 0 my ($self, @args) = @_;
332 0         0 return $self->send( HEAD => @args );
333             }
334              
335             sub delete {
336 0     0 0 0 my ($self, @args) = @_;
337 0         0 return $self->send( DELETE => @args );
338             }
339              
340             sub _execute_callbacks {
341 20     20   83 my ($self, $type, %options) = @_;
342              
343 20         47 my $sth = $options{$type};
344              
345 20         92 while (my ($key, $callback) = each %$sth) {
346 89 50       195 next if !defined $callback;
347 89 100       380 next if !UNIVERSAL::isa($callback, 'CODE');
348 16         65 $sth->{$key} = $self->$callback(key => $key, %options);
349             }
350             }
351              
352             sub send {
353 10     10 0 35 my ($self, $method, $path,
354             $data, $headers, $events) = @_;
355              
356 10         34 $method = uc $method;
357 10         39 $data = _defor( $data, {} );
358 10         34 $headers = _defor( $headers, {} );
359 10         45 $events = _defor( $events, {} );
360              
361 10         251 my $base_url = $self->base_url;
362 10 100       228 my $url = $base_url ? $base_url . $path : $path;
363 10         272 my $ua = $self->ua;
364 10         216 my $retry_count = _defor( $self->retry->{count}, 1 );
365 10         206 my $retry_delay = _defor( $self->retry->{delay}, 5 );
366 10         27 my %retry_status = %{ _defor($self->retry->{status}, {}) };
  10         248  
367 10         40 my %debug = %{ _defor($self->debug_flags, {}) };
  10         243  
368 10         210 my $eng = $self->engine;
369              
370 10 50       237 if ( my $pd = $self->pre_defined_data ) {
371 10         122 %$data = ( %$pd, %$data );
372             }
373              
374 10 50       191 if ( my $ph = $self->pre_defined_headers ) {
375 10         62 %$headers = ( %$ph, %$headers );
376             }
377              
378 10 50       175 if ( my $pe = $self->pre_defined_events ) {
379 10         79 %$events = ( %$pe, %$events );
380             }
381              
382 10         73 my %options = (
383             method => \$method,
384             url => \$url,
385             path => \$path,
386             data => $data,
387             headers => $headers,
388             events => $events,
389             );
390              
391 10         55 $self->_execute_callbacks(data => %options);
392 10         66 $self->_execute_callbacks(headers => %options);
393              
394 10         30 my $response;
395              
396             RETRY:
397 10         42 foreach my $retry ( 0 .. $retry_count ) {
398 10         25 my $started_time = time;
399              
400 10 50       44 if ( $eng eq 'LWP::UserAgent' ) {
401 10         59 my $req = $self->new_request( %options );
402              
403 10 50       50 if ($events->{test_request_object}) {
404 10         89 return $req;
405             }
406              
407 0         0 $response = $ua->request($req);
408             }
409              
410 0 0 0     0 if ( $debug{in_out} || $debug{send_out} ) {
411 0         0 print STDERR "-- REQUEST --\n";
412 0 0 0     0 if ( $retry_count && $retry ) {
413 0         0 print STDERR "-- RETRY $retry of $retry_count\n";
414             }
415 0         0 print STDERR $response->request->as_string;
416 0         0 print STDERR "\n";
417             }
418              
419 0         0 my $debug_response = _defor($debug{in_out}, $debug{response});
420              
421             $debug_response = 0
422 0 0 0     0 if $debug{response_if_fail} && $response->is_success;
423              
424 0 0       0 if ($debug_response) {
425 0         0 my $used_time = time - $started_time;
426              
427 0         0 print STDERR "-- RESPONSE $used_time sec(s) --\n";
428              
429             print STDERR $debug{response_header_only}
430 0 0       0 ? $response->headers->as_string
431             : $response->as_string;
432              
433 0         0 print STDERR ( "-" x 80 ) . "\n";
434             }
435              
436             last RETRY ## request is success, not further for retry
437 0 0       0 if $response->is_success;
438              
439 0 0       0 if ( !%retry_status ) {
440 0         0 sleep $retry_delay;
441             ## no retry pattern at all then just retry
442 0         0 next RETRY;
443             }
444              
445 0 0       0 my $pattern = $retry_status{ $response->code }
446             or
447             last RETRY; ## no retry pattern for this status code, just stop retry
448              
449             ## retry if pattern is match otherwise, just stop retry
450 0 0       0 if ( $response->decode_content =~ /$pattern/ ) {
451 0         0 sleep $retry_delay;
452 0         0 next RETRY;
453             }
454              
455 0         0 last RETRY;
456             }
457              
458 0         0 return $self->last_response($response);
459             }
460              
461             sub json_response {
462 0     0 0 0 my ($self) = @_;
463              
464             my $response = try {
465 0     0   0 my $content = _defor($self->last_response->decoded_content, '{}');
466 0         0 $self->json->decode($content);
467             }
468             catch {
469 0     0   0 my $error = $_;
470 0         0 { status => "error", error => $error };
471 0         0 };
472              
473 0         0 return $response;
474             }
475              
476             sub kvp_response {
477 0     0 0 0 my ($self) = @_;
478              
479 0 0       0 my $content = $self->last_response->decoded_content
480             or return {};
481              
482             my %data = map {
483 0         0 my ( $k, $v ) = map { uri_unescape($_) } split /=/, $_, 2;
  0         0  
  0         0  
484             } split /&/, $content;
485              
486 0         0 return \%data;
487             }
488              
489             sub new_request {
490 10     10 0 43 my ($self, %o) = @_;
491              
492 10         58 my ($method, $url) = map { $$_ } @o{qw(method url)};
  20         75  
493              
494 10         54 my ($data, $headers, $events) = @o{qw(data headers events)};
495              
496 10         57 my $content_type = $self->get_content_type(%o);
497              
498 10         50 my $content = $self->convert_data(%o);
499              
500 10 100       61 if ($content) {
501 9 50       217 if ($self->charset eq 'utf8') {
502 9         71 $content = _tune_utf8($content);
503             }
504             }
505              
506 10         35 my $request;
507              
508 10 100       50 if ($method eq 'GET') {
    50          
509 5 50       27 if ($content_type ne 'application/x-www-form-urlencoded') {
    100          
510 0         0 die "Unable to create a get request with content_type: $content_type";
511             }
512             elsif ($content) {
513 4 50       16 if ($url =~ m/\?/) {
514 0         0 $request = $self->prepare_request(%o, url => \"$url&$content");
515             }
516             else {
517 4         28 $request = $self->prepare_request(%o, url => \"$url?$content");
518             }
519             }
520             else {
521 1         7 $request = $self->prepare_request(%o);
522             }
523             }
524             elsif ($content) {
525 5         31 $request = $self->prepare_request(%o);
526 5         25 $request->content($content);
527             }
528              
529 10         165 %o = (%o,
530             request => $request,
531             content => \$content,
532             );
533              
534 10 50       59 if (my $do = $events->{before_headers}) {
535 0         0 $self->$do(%o);
536             }
537              
538 10         21 my @keys;
539              
540 10 50       45 if (my $keys = $events->{headers_keys}) {
    50          
541 0         0 @keys = $self->$keys(%o);
542             }
543             elsif (my $add = $events->{add_headers_keys}) {
544 0         0 @keys = sort $self->$add(%o), keys %$headers;
545             }
546             else {
547 10         41 @keys = sort keys %$headers;
548             }
549              
550 10         28 foreach my $key ( @keys ) {
551 2 50       9 if (my $do = $events->{before_header}{$key}) {
552 0         0 $headers->{$key} = $self->$do(%o);
553             }
554              
555 2 50 33     14 next if $o{skip_headers}{$key} || !exists $headers->{$key} || !defined $headers->{$key};
      33        
556              
557 2         14 $request->header( $key => $headers->{$key} );
558              
559 2 50       148 if (my $do = $events->{after_header}{$key}) {
560 0         0 $self->$do(%o);
561             }
562             }
563              
564 10 100       32 if (my $do = $events->{after_header_keys}) {
565 1         5 $self->$do(%o);
566             }
567              
568 10         56 return $request;
569             }
570              
571             sub prepare_request {
572 10     10 0 51 my ($self, %o) = @_;
573              
574 10         36 my ($method, $url) = map { $$_ } @o{qw(method url)};
  20         56  
575              
576 10         29 my ($headers) = @o{qw(headers)};
577              
578 10         50 my $request = HTTP::Request->new( $method => $url );
579              
580 10         7062 $request->content_type($self->get_content_type(%o));
581              
582 10         420 my ($u, $p, $at) = map { _defor($self->$_, '') }
  30         579  
583             qw(username password auth_token);
584              
585 10 100 66     65 if ($u || $p) {
    50          
586 1         5 $self->basic_authenticator($request, $u, $p);
587             }
588             elsif ($at) {
589 0         0 $headers->{authorization} = $at;
590             }
591              
592 10         147 return $request;
593             }
594              
595             sub _tune_utf8 {
596 9     9   23 my ($content) = @_;
597              
598 9         64 my $req = HTTP::Request->new( POST => "http://find-encoding.com" );
599              
600             try {
601 9     9   584 $req->content($content);
602             }
603             catch {
604 0     0   0 my $error = $_;
605 0 0       0 if ( $error =~ /content must be bytes/ ) {
606 0         0 eval { $content = Encode::encode( utf8 => $content ); };
  0         0  
607             }
608 9         17317 };
609 9         533 return $content;
610             }
611              
612             sub convert_data {
613 10     10 0 37 my ($self, %o) = @_;
614              
615 10         31 my ($data, $events) = @o{qw(data events)};
616              
617 10         45 my $content_type = $self->get_content_type(%o);
618              
619 10 100       59 if ($content_type =~ m/json/) {
    50          
620 3         27 return $self->kvp2json(%o);
621             }
622             elsif ($content_type eq 'application/x-www-form-urlencoded') {
623 7         43 return $self->kvp2str(%o);
624             }
625             else {
626 0         0 return $data;
627             }
628             }
629              
630             sub kvp2json {
631 3     3 0 13 my ($self, %o) = @_;
632              
633 3         12 my ($data, $events) = @o{qw(data events)};
634              
635 3         7 my @keys;
636              
637 3 100       14 if (my $do = $events->{keys}) {
638 1         6 @keys = $self->$do(%o);
639             }
640             else {
641 2         9 @keys = keys %$data;
642             }
643              
644 3         11 my %data = ();
645              
646 3         10 foreach my $key(@keys) {
647 14 50       73 if ($events->{not_include}{$key}) {
648             next
649 0         0 }
650 14 50 33     88 next if $o{skip_key}{$key} || !exists $data->{$key} || !defined $data->{$key};
      33        
651 14         110 $data{$key} = $self->kvp2json_each(%o, value => $data->{$key});
652             }
653              
654 3         83 return $self->json->encode(\%data);
655             }
656              
657             sub kvp2json_each {
658 43     43 0 165 my ($self, %o) = @_;
659              
660 43         101 my ($v) = map { _defor($_, '') } @o{qw( value )};
  43         65  
661              
662 43 100       145 if (UNIVERSAL::isa($v, 'CODE')) {
663 5         36 $v = $self->$v(%o);
664             }
665              
666 43 100       181 if (!ref $v) {
    100          
    100          
    50          
667 26 50       125 return looks_like_number($v) ? $v+0 : $v;
668             }
669             elsif (ref $v eq 'BOOL') {
670 6         24 return $v->[0];
671             }
672             elsif (UNIVERSAL::isa($v, 'ARRAY')) {
673 9         12 my @parts;
674              
675 9         17 foreach my $val(@$v) {
676 23         83 push @parts, $self->kvp2json_each(%o, value => $val);
677             }
678              
679 9         31 return \@parts;
680             }
681             elsif (UNIVERSAL::isa($v, 'HASH')) {
682 2         4 my %parts;
683              
684 2         7 foreach my $key(keys %$v) {
685 6         20 $parts{$key} = $self->kvp2json_each(%o, value => $v->{$key});
686             }
687              
688 2         8 return \%parts;
689             }
690              
691 0         0 return $v;
692             }
693              
694             sub kvp2str {
695 8     8 0 63 my ($self, %o) = @_;
696              
697 8         24 my ($data, $events) = @o{qw(data events)};
698              
699 8         31 my @keys;
700              
701 8 50       48 if (my $do = $events->{before_sorting_keys}) {
702 0         0 $self->$do(%o, keys => \@keys);
703             }
704              
705 8 100       25 if (my $do = $events->{keys}) {
706 2         7 @keys = $self->$do(%o);
707             }
708             else {
709 6         49 @keys = sort keys %$data;
710             }
711              
712 8 50       36 if (my $do = $events->{after_sorting_keys}) {
713 0         0 $self->$do(%o, keys => \@keys);
714             }
715              
716 8         47 my @parts;
717              
718 8         30 foreach my $key(@keys) {
719 41 50 66     227 next if $o{skip_key}{$key} || !exists $data->{$key} || !defined $data->{$key};
      66        
720 40         131 push @parts, $self->kvp2str_each(%o, key => $key, value => $data->{$key});
721             }
722              
723 8         80 return join '&', @parts;
724             }
725              
726             sub kvp2str_each {
727 82     82 0 301 my ($self, %o) = @_;
728              
729 82         147 my ($k, $v) = map { _defor($_, '') } @o{qw( key value )};
  164         268  
730              
731 82         191 $k = uri_escape($k);
732              
733 82 100       1072 if (UNIVERSAL::isa($v, 'CODE')) {
734 6         25 $v = $self->$v(%o, key => $k);
735             }
736              
737 82 100       204 if (!ref $v) {
    100          
    100          
    50          
738 48         86 $v = uri_escape($v);
739              
740 48 100       615 $v = $v + 0 if looks_like_number($v);
741              
742 48 100       100 if ($o{no_key}) {
743 16         44 return $v;
744             }
745             else {
746 32         135 return "$k=$v";
747             }
748             }
749             elsif (ref $v eq 'BOOL') {
750 18 100       84 return ref $v->[0] eq 'SCALAR'
751 4         17 ? "$k=${$v->[0]}"
752             : "$k=$v->[0]";
753              
754             }
755             elsif (ref $v eq 'ARRAY') {
756 10         12 my @parts;
757              
758 10         20 foreach my $val(@$v) {
759 24         78 push @parts, $self->kvp2str_each(%o, key => $k, value => $val, no_key => 0);
760             }
761              
762 10 100       51 return ($o{no_key} ? '&' : '') . join '&', @parts;
763             }
764             elsif (ref $v eq 'CSV') {
765 6         11 my @csv;
766             my @parts;
767              
768 6         11 foreach my $val(@$v) {
769 18         62 my $part = $self->kvp2str_each(%o, key => $k, value => $val, no_key => 1);
770              
771 18 100       51 if ($part =~ m/&/) {
772 2         5 push @parts, $part;
773             }
774             else {
775 16         32 push @csv, $part;
776             }
777             }
778              
779 6         17 my $csv = "$k=".join( ',', @csv);
780            
781 6 100       15 if (@parts) {
782 2         10 return join '&', $csv, @parts;
783             }
784              
785 4         16 return $csv;
786             }
787              
788 0         0 return $v;
789             }
790              
791             sub basic_authenticator {
792 1     1 0 4 my ($self, $req, $u, $p) = @_;
793 1         6 return $req->headers->authorization_basic($u, $p);
794             }
795              
796             sub _defor {
797 370     370   929 my ($default, $or) = @_;
798 370 100 100     1798 return (defined($default) && length($default)) ? $default : $or;
799             }
800              
801 7     7   114 no Moo;
  7         24  
  7         81  
802              
803             1;