File Coverage

blib/lib/WebService/DigitalOcean/Role/UserAgent.pm
Criterion Covered Total %
statement 43 56 76.7
branch 5 10 50.0
condition 0 2 0.0
subroutine 10 12 83.3
pod 1 1 100.0
total 59 81 72.8


line stmt bran cond sub pod time code
1             package WebService::DigitalOcean::Role::UserAgent;
2             # ABSTRACT: User Agent Role for DigitalOcean WebService
3 2     2   7176 use Moo::Role;
  2         3  
  2         13  
4 2     2   465 use LWP::UserAgent;
  2         4  
  2         36  
5 2     2   6 use JSON ();
  2         4  
  2         31  
6 2     2   7 use DateTime;
  2         2  
  2         35  
7 2     2   10 use Types::Standard qw/is_HashRef/;
  2         2  
  2         34  
8 2     2   738 use utf8;
  2         3  
  2         12  
9              
10             our $VERSION = '0.025'; # VERSION
11              
12             has ua => (
13             is => 'lazy',
14             );
15              
16             sub _build_ua {
17 0     0   0 my ($self) = @_;
18              
19 0   0     0 my $version = __PACKAGE__->VERSION || 'devel';
20              
21 0         0 my @headers = (
22             'Authorization' => 'Bearer ' . $self->token,
23             'Content-Type' => 'application/json; charset=utf-8',
24             );
25              
26 0         0 my $ua = LWP::UserAgent->new(
27             agent => 'WebService::DigitalOcean/' . $version,
28             default_headers => HTTP::Headers->new(@headers),
29             );
30              
31 0         0 $ua->env_proxy;
32              
33 0         0 return $ua;
34             }
35              
36             sub _build_request {
37 1     1   2 my ( $self, $method, $uri, $data ) = @_;
38              
39 1         6 my $full_url = $self->api_base_url . $uri;
40 1 50       21 my $encoded_data = $data ? JSON::encode_json($data) : undef;
41 1         2 my $headers = undef;
42              
43 1         12 return HTTP::Request->new( $method, $full_url, $headers, $encoded_data );
44             }
45              
46             sub _send_request {
47 0     0   0 my ($self, $request) = @_;
48              
49 0         0 my $response = $self->ua->request($request);
50              
51 0         0 return $response;
52             }
53              
54             sub make_request {
55 1     1 1 2 my ($self, $method, $uri, $data) = @_;
56              
57 1         4 my $request = $self->_build_request( $method, $uri, $data );
58 1         5680 my $response = $self->_send_request($request);
59              
60 1         12 my $result = {
61             request_object => $request,
62             response_object => $response,
63             is_success => $response->is_success,
64             status_line => $response->status_line,
65             };
66              
67 1         35 my $ratelimit = $self->_get_ratelimit( $response->headers );
68              
69 1         409 my $content = $self->_get_content(
70             # avoid ``wantarray`` problems by setting scalar context
71             my $ct = $response->content_type,
72             my $dc = $response->decoded_content,
73             );
74              
75 1         8 return { %$result, %$ratelimit, %$content };
76             }
77              
78             sub _get_ratelimit {
79 1     1   6 my ($self, $headers) = @_;
80              
81 1         5 my $limit = $headers->header('RateLimit-Limit');
82              
83 1 50       42 if (!$limit) {
84 0         0 return {};
85             }
86              
87             return {
88 1         3 ratelimit => {
89             limit => $limit,
90             remaining => $headers->header('RateLimit-Remaining'),
91             reset => DateTime->from_epoch(
92             epoch => $headers->header('RateLimit-Reset')
93             ),
94             }
95             };
96             }
97              
98             sub _get_content {
99 1     1   190 my ($self, $content_type, $content) = @_;
100              
101 1 50       5 if ($content_type ne 'application/json') {
102 0         0 warn "Unexpected Content-Type " . $content_type;
103 0         0 return {};
104             }
105              
106 1         25 my $decoded_response = JSON::decode_json( $content );
107              
108 1 50       7 if ( !is_HashRef($decoded_response) ) {
109 0         0 return { content => $decoded_response };
110             }
111              
112 1         2 my $meta = delete $decoded_response->{meta};
113 1         2 my $links = delete $decoded_response->{links};
114              
115 1         3 my @values = values %$decoded_response;
116              
117 1 50       3 my $c = scalar @values == 1
118             ? $values[0]
119             : $decoded_response
120             ;
121              
122             return {
123 1         5 meta => $meta,
124             links => $links,
125             content => $c,
126             };
127             }
128              
129             1;
130              
131             __END__
132              
133             =pod
134              
135             =encoding UTF-8
136              
137             =head1 NAME
138              
139             WebService::DigitalOcean::Role::UserAgent - User Agent Role for DigitalOcean WebService
140              
141             =head1 VERSION
142              
143             version 0.025
144              
145             =head1 DESCRIPTION
146              
147             Role used to make requests to the DigitalOcean API, and to format their response.
148              
149             =head1 METHODS
150              
151             =head2 make_request
152              
153             my $res = $self->make_request(POST => '/domains', {
154             name => 'example.com',
155             ip_address => '12.34.56.78',
156             });
157              
158             =head3 Arguments
159              
160             =over
161              
162             =item C<Str> $method
163              
164             The HTTP verb, such as POST, GET, PUT, etc.
165              
166             =item C<Str> $path
167              
168             Path to the resource in the URI, to be prepended with $self->api_base_url.
169              
170             =item C<HashRef> $data (optional)
171              
172             The content to be JSON encoded and sent to DigitalOcean's API.
173              
174             =back
175              
176             =head3 Returns
177              
178             HashRef containing:
179              
180             =over
181              
182             =item L<HTTP::Response> response_object
183              
184             =item C<Bool> is_success
185              
186             Shortcut to $res->{response_object}{is_success}.
187              
188             =item C<Str> status_line
189              
190             Shortcut to $res->{response_object}{status_line}.
191              
192             =item C<HashRef> content
193              
194             The JSON decoded content the API has responded with.
195              
196             =item C<HashRef> ratelimit
197              
198             RateLimit headers parsed.
199              
200             =over
201              
202             =item C<Int> limit
203              
204             =item C<Int> remaining
205              
206             =item L<DateTime> reset
207              
208             =back
209              
210             =back
211              
212             Makes requests to the DigitalOcean, and parses the response.
213              
214             All requests made from other methods use L</make_request> to make them.
215              
216             More info: L<< https://developers.digitalocean.com/#introduction >>.
217              
218             =head1 AUTHOR
219              
220             André Walker <andre@cpan.org>
221              
222             =head1 COPYRIGHT AND LICENSE
223              
224             This software is Copyright (c) 2015 by André Walker.
225              
226             This is free software, licensed under:
227              
228             The GNU General Public License, Version 2, June 1991
229              
230             =cut