File Coverage

blib/lib/WWW/Hetzner/Role/HTTP.pm
Criterion Covered Total %
statement 67 67 100.0
branch 13 14 92.8
condition 10 12 83.3
subroutine 16 16 100.0
pod 4 4 100.0
total 110 113 97.3


line stmt bran cond sub pod time code
1             package WWW::Hetzner::Role::HTTP;
2              
3             # ABSTRACT: HTTP client role for Hetzner API clients
4              
5 25     25   282551 use Moo::Role;
  25         69  
  25         192  
6 25     25   25717 use WWW::Hetzner::HTTPRequest;
  25         96  
  25         1062  
7 25     25   1489 use WWW::Hetzner::HTTPResponse;
  25         57  
  25         776  
8 25     25   12248 use WWW::Hetzner::LWPIO;
  25         215  
  25         1245  
9 25     25   3302 use JSON::MaybeXS qw(decode_json encode_json);
  25         34680  
  25         4022  
10 25     25   668 use Carp qw(croak);
  25         288  
  25         1964  
11 25     25   12342 use Log::Any qw($log);
  25         234469  
  25         150  
12              
13             our $VERSION = '0.100';
14              
15              
16             requires 'token';
17             requires 'base_url';
18              
19             has io => (
20             is => 'lazy',
21 1     1   4257 builder => sub { WWW::Hetzner::LWPIO->new },
22             );
23              
24              
25             sub get {
26 69     69 1 16623 my ($self, $path, %params) = @_;
27 69         2219 return $self->_request('GET', $path, %params);
28             }
29              
30              
31             sub post {
32 54     54 1 6020 my ($self, $path, $data) = @_;
33 54         1569 return $self->_request('POST', $path, body => $data);
34             }
35              
36              
37             sub put {
38 4     4 1 16 my ($self, $path, $data) = @_;
39 4         120 return $self->_request('PUT', $path, body => $data);
40             }
41              
42              
43             sub delete {
44 12     12 1 41 my ($self, $path) = @_;
45 12         370 return $self->_request('DELETE', $path);
46             }
47              
48              
49             sub _set_auth {
50 129     129   332 my ($self, $headers) = @_;
51 129         611 $headers->{Authorization} = 'Bearer ' . $self->token;
52             }
53              
54              
55             sub _build_request {
56 142     142   17684 my ($self, $method, $path, %opts) = @_;
57              
58 142         532 my $url = $self->base_url . $path;
59              
60             # Add query params for GET
61 142 100 100     725 if ($method eq 'GET' && $opts{params}) {
62 36         70 my @pairs;
63 36         68 for my $k (keys %{$opts{params}}) {
  36         139  
64 10         26 my $v = $opts{params}{$k};
65 10 100       33 next unless defined $v;
66 9         33 push @pairs, "$k=$v";
67             }
68 36 100       161 $url .= '?' . join('&', @pairs) if @pairs;
69             }
70              
71 142         1120 $log->debug("$method $url");
72              
73 142         834 my %headers;
74 142         617 $self->_set_auth(\%headers);
75 142         402 $headers{'Content-Type'} = 'application/json';
76              
77 142         702 my %req_args = (
78             method => $method,
79             url => $url,
80             headers => \%headers,
81             );
82              
83 142 100       523 if ($opts{body}) {
84 59         553 $req_args{content} = encode_json($opts{body});
85 59         270 $log->debugf("Body: %s", $req_args{content});
86             }
87              
88 142         3903 return WWW::Hetzner::HTTPRequest->new(%req_args);
89             }
90              
91              
92             sub _parse_response {
93 142     142   15888 my ($self, $response, $method, $path) = @_;
94              
95 142         1000 $log->debugf("Response: %s", $response->status);
96              
97 142         856 my $data;
98 142 100 100     1448 if ($response->content && $response->content =~ /^\s*[\{\[]/) {
99 140         2373 $data = decode_json($response->content);
100             }
101              
102 142 100 66     920 unless ($response->status >= 200 && $response->status < 300) {
103 4   66     23 my $error = $data->{error}{message} // $response->status;
104 4         28 $log->errorf("API error: %s", $error);
105 4         1080 croak "Hetzner API error: $error";
106             }
107              
108 138         829 $log->infof("%s %s -> %s", $method, $path, $response->status);
109 138         2026 return $data;
110             }
111              
112              
113             sub _request {
114 139     139   566 my ($self, $method, $path, %opts) = @_;
115              
116 139 50       479 croak "No API token configured" unless $self->token;
117              
118 139         577 my $req = $self->_build_request($method, $path, %opts);
119 139         42745 my $response = $self->io->call($req);
120 138         122699 return $self->_parse_response($response, $method, $path);
121             }
122              
123              
124             1;
125              
126             __END__