File Coverage

blib/lib/Apertur/SDK/HTTPClient.pm
Criterion Covered Total %
statement 42 95 44.2
branch 3 34 8.8
condition 0 9 0.0
subroutine 12 15 80.0
pod 3 3 100.0
total 60 156 38.4


line stmt bran cond sub pod time code
1             package Apertur::SDK::HTTPClient;
2              
3 1     1   24 use strict;
  1         3  
  1         42  
4 1     1   5 use warnings;
  1         2  
  1         38  
5              
6 1     1   808 use LWP::UserAgent;
  1         51379  
  1         41  
7 1     1   8 use HTTP::Request;
  1         2  
  1         23  
8 1     1   548 use HTTP::Request::Common qw(POST);
  1         2239  
  1         78  
9 1     1   786 use JSON qw(encode_json decode_json);
  1         10121  
  1         6  
10              
11 1     1   664 use Apertur::SDK::Error;
  1         2  
  1         27  
12 1     1   408 use Apertur::SDK::Error::Authentication;
  1         2  
  1         23  
13 1     1   356 use Apertur::SDK::Error::NotFound;
  1         3  
  1         24  
14 1     1   345 use Apertur::SDK::Error::RateLimit;
  1         2  
  1         22  
15 1     1   569 use Apertur::SDK::Error::Validation;
  1         2  
  1         870  
16              
17             sub new {
18 3     3 1 15 my ($class, %args) = @_;
19 3         6 my $base_url = $args{base_url};
20 3         15 $base_url =~ s{/+$}{};
21              
22 3         5 my $auth_header = '';
23 3 100       9 if ($args{api_key}) {
    50          
24 2         4 $auth_header = "Bearer $args{api_key}";
25             }
26             elsif ($args{oauth_token}) {
27 1         3 $auth_header = "Bearer $args{oauth_token}";
28             }
29              
30 3         25 my $ua = LWP::UserAgent->new(
31             agent => 'Apertur-SDK-Perl/0.01',
32             timeout => 30,
33             );
34              
35 3         3629 return bless {
36             base_url => $base_url,
37             auth_header => $auth_header,
38             ua => $ua,
39             }, $class;
40             }
41              
42             sub request {
43 0     0 1   my ($self, $method, $path, %opts) = @_;
44              
45 0           my $url = $self->{base_url} . $path;
46 0   0       my $headers = $opts{headers} || {};
47              
48             $headers->{'Authorization'} = $self->{auth_header}
49 0 0         if $self->{auth_header};
50              
51 0           my $req;
52 0 0         if ($opts{multipart}) {
53             # Multipart form upload
54             $req = POST(
55             $url,
56             Content_Type => 'form-data',
57             Content => $opts{multipart},
58 0           );
59             # Apply auth header on top of the multipart request
60             $req->header('Authorization' => $headers->{'Authorization'})
61 0 0         if $headers->{'Authorization'};
62             # Apply any extra headers
63 0           for my $key (keys %$headers) {
64 0 0         next if $key eq 'Authorization';
65 0           $req->header($key => $headers->{$key});
66             }
67             }
68             else {
69 0           $req = HTTP::Request->new(uc($method), $url);
70 0           for my $key (keys %$headers) {
71 0           $req->header($key => $headers->{$key});
72             }
73 0 0         if (defined $opts{body}) {
74 0   0       $req->content_type($headers->{'Content-Type'} // 'application/json');
75 0           $req->content($opts{body});
76             }
77             }
78              
79 0           my $res = $self->{ua}->request($req);
80 0           my $status = $res->code;
81              
82 0 0         if ($status >= 400) {
83 0           $self->_handle_error($res);
84             }
85              
86 0 0         if ($status == 204) {
87 0           return undef;
88             }
89              
90 0           return decode_json($res->decoded_content);
91             }
92              
93             sub request_raw {
94 0     0 1   my ($self, $method, $path, %opts) = @_;
95              
96 0           my $url = $self->{base_url} . $path;
97 0   0       my $headers = $opts{headers} || {};
98              
99             $headers->{'Authorization'} = $self->{auth_header}
100 0 0         if $self->{auth_header};
101              
102 0           my $req = HTTP::Request->new(uc($method), $url);
103 0           for my $key (keys %$headers) {
104 0           $req->header($key => $headers->{$key});
105             }
106              
107 0           my $res = $self->{ua}->request($req);
108 0           my $status = $res->code;
109              
110 0 0         if ($status >= 400) {
111 0           $self->_handle_error($res);
112             }
113              
114 0           return $res->content;
115             }
116              
117             sub _handle_error {
118 0     0     my ($self, $res) = @_;
119 0           my $status = $res->code;
120              
121 0           my $body = {};
122 0           eval {
123 0           $body = decode_json($res->decoded_content);
124             };
125 0 0         if ($@) {
126 0           $body = { message => "HTTP $status" };
127             }
128              
129 0   0       my $message = $body->{message} || "HTTP $status";
130 0           my $code = $body->{code};
131              
132 0 0         if ($status == 401) {
    0          
    0          
    0          
133 0           Apertur::SDK::Error::Authentication->throw(message => $message);
134             }
135             elsif ($status == 404) {
136 0           Apertur::SDK::Error::NotFound->throw(message => $message);
137             }
138             elsif ($status == 429) {
139 0           my $retry_after = $res->header('Retry-After');
140 0 0         $retry_after = defined $retry_after ? int($retry_after) : undef;
141 0           Apertur::SDK::Error::RateLimit->throw(
142             message => $message,
143             retry_after => $retry_after,
144             );
145             }
146             elsif ($status == 400) {
147 0           Apertur::SDK::Error::Validation->throw(message => $message);
148             }
149             else {
150 0           Apertur::SDK::Error->throw(
151             status_code => $status,
152             code => $code,
153             message => $message,
154             );
155             }
156             }
157              
158             1;
159              
160             __END__