File Coverage

blib/lib/DigitalOcean.pm
Criterion Covered Total %
statement 54 215 25.1
branch 0 26 0.0
condition 0 18 0.0
subroutine 18 54 33.3
pod 20 20 100.0
total 92 333 27.6


line stmt bran cond sub pod time code
1 1     1   12422 use strict;
  1         2  
  1         27  
2             package DigitalOcean;
3 1     1   391 use Mouse;
  1         18913  
  1         4  
4              
5 1     1   514 use DigitalOcean::Response;
  1         2  
  1         23  
6 1     1   343 use DigitalOcean::Droplet;
  1         2  
  1         24  
7 1     1   299 use DigitalOcean::Meta;
  1         2  
  1         22  
8 1     1   292 use DigitalOcean::Links;
  1         1  
  1         21  
9 1     1   318 use DigitalOcean::Collection;
  1         2  
  1         25  
10 1     1   300 use DigitalOcean::Account;
  1         2  
  1         23  
11 1     1   304 use DigitalOcean::Action;
  1         1  
  1         28  
12 1     1   310 use DigitalOcean::Domain;
  1         1  
  1         22  
13 1     1   313 use DigitalOcean::Droplet::Upgrade;
  1         1  
  1         21  
14 1     1   345 use DigitalOcean::SSH::Key;
  1         1  
  1         23  
15              
16             #for requesting
17 1     1   550 use LWP::UserAgent;
  1         30648  
  1         26  
18 1     1   392 use LWP::Protocol::https;
  1         66965  
  1         34  
19              
20             #for dealing with JSON
21 1     1   617 use JSON::XS;
  1         3602  
  1         70  
22              
23             #for printing pretty deaths
24 1     1   504 use Data::Dumper qw//;
  1         3823  
  1         24  
25              
26             #DigitalOcean packages
27 1     1   302 use DigitalOcean::Error;
  1         2  
  1         5  
28              
29             #ABSTRACT: An OO interface to the Digital Ocean API (v2).
30              
31             has oauth_token => (
32             is => 'rw',
33             isa => 'Str',
34             required => 1,
35             );
36              
37             has ua => (
38             is => 'ro',
39             isa => 'LWP::UserAgent',
40             required => 0,
41             default => sub { LWP::UserAgent->new },
42             );
43              
44             has api => (
45             is => 'ro',
46             isa => 'Str',
47             default => 'https://api.digitalocean.com/v2/',
48             required => 0,
49             );
50              
51             has 'time_between_requests' => (
52             is => 'rw',
53             isa => 'Int',
54             default => 2,
55             required => 0,
56             );
57              
58             has 'wait_on_actions' => (
59             is => 'rw',
60             isa => 'Bool',
61             default => undef,
62             required => 0,
63             );
64              
65              
66             has ratelimit_limit => (
67             is => 'rw',
68             );
69              
70              
71             has ratelimit_remaining => (
72             is => 'rw',
73             );
74              
75              
76             has ratelimit_reset => (
77             is => 'rw',
78             );
79              
80             #define constants for HTTP request types
81             use constant {
82 1         1566 GET => 'GET',
83             DELETE => 'DELETE',
84             PUT => 'PUT',
85             POST => 'POST',
86             HEAD => 'HEAD',
87 1     1   101 };
  1         1  
88              
89              
90             has die_pretty => (
91             is => 'rw',
92             isa => 'Bool',
93             default => 1,
94             );
95              
96              
97             has last_response => (
98             is => 'rw',
99             isa => 'Undef|DigitalOcean::Response',
100             default => undef,
101             );
102              
103              
104             has per_page => (
105             is => 'rw',
106             isa =>'Undef|Int',
107             default => undef,
108             );
109              
110             sub _request {
111 0     0     my $self = shift;
112 0           my (%args) = @_;
113 0           my ($req_method, $path, $params, $req_body_hash, $type) = ($args{req_method}, $args{path}, $args{params}, $args{req_body_hash}, $args{type});
114            
115             #create request
116 0           my $uri = URI->new($self->api . $path);
117              
118             #assign per_page if global value is set and one was not passed in
119 0 0 0       if(not $params->{per_page} and $self->per_page) {
120 0           $params->{per_page} = $self->per_page;
121             }
122              
123 0           $uri->query_form($params);
124              
125 0           my $req = HTTP::Request->new(
126             $req_method,
127             $uri,
128             );
129              
130             #add authentication
131 0           $req->header(Authorization => 'Bearer ' . $self->oauth_token);
132              
133 0           my $wait_on_action;
134              
135             #set body content
136 0 0         if($req_body_hash) {
137             #get wait on action out if it was passed in
138 0           $wait_on_action = delete $req_body_hash->{wait_on_action};
139              
140             #set json header
141 0           $req->header('Content-Type' => 'application/json');
142              
143             #put json in body
144 0           my $json_coder = JSON::XS->new->ascii->allow_nonref;
145 0           my $req_body = $json_coder->encode($req_body_hash);
146 0           $req->content($req_body);
147             }
148              
149 0           my $response = $self->ua->request($req);
150 0           my $json;
151 0 0         if($response->content) {
152 0           $json = JSON::XS->new->utf8->decode($response->content);
153              
154             #die with DigitalOcean::Error
155 0 0 0       if($response->code < 200 or $response->code >= 300) {
156             my $do_error = DigitalOcean::Error->new(
157             id => $json->{id},
158             message => $json->{message},
159 0           status_code => $response->code,
160             status_message => $response->message,
161             status_line => $response->status_line,
162             );
163              
164 0 0         die $self->die_pretty ? Data::Dumper->Dump([$do_error, $self]) : $do_error;
165             }
166              
167             }
168              
169 0           my $do_response = DigitalOcean::Response->new(
170             json => $json,
171             status_code => $response->code,
172             status_message => $response->message,
173             status_line => $response->status_line,
174             );
175              
176 0 0 0       if($json and ref($json) eq 'HASH') {
177             #add meta object if one was passed back
178 0 0         $do_response->meta(DigitalOcean::Meta->new(%{$json->{meta}})) if $json->{meta};
  0            
179              
180 0 0         if($json->{links}) {
181             #add links object if one was passed back
182 0           $do_response->links(DigitalOcean::Links->new(%{$json->{links}}));
  0            
183              
184             #if actions array is present and we are supposed to wait on events, then wait!
185 0 0 0       if($json->{links}->{actions} and ($self->wait_on_actions or $wait_on_action)) {
      0        
186             #wait on each returned action that occurred from the API call
187 0           for my $act_temp (@{$json->{links}->{actions}}) {
  0            
188 0           my $action = $self->action($act_temp->{id});
189              
190             #wait on action
191 0           $action->wait;
192             }
193             }
194             }
195             }
196              
197 0           $self->last_response($do_response);
198              
199             #parse ratelimit headers
200 0           $self->ratelimit_limit($response->header('RateLimit-Limit'));
201 0           $self->ratelimit_remaining($response->header('RateLimit-Remaining'));
202 0           $self->ratelimit_reset($response->header('RateLimit-Reset'));
203              
204 0           return $do_response;
205             }
206              
207             sub _GET {
208 0     0     my $self = shift;
209 0           my (%args) = @_;
210 0           $args{req_method} = GET;
211              
212 0           return $self->_request(%args);
213             }
214              
215             sub _POST {
216 0     0     my $self = shift;
217 0           my (%args) = @_;
218 0           $args{req_method} = POST;
219              
220 0           return $self->_request(%args);
221             }
222              
223             sub _DELETE {
224 0     0     my $self = shift;
225 0           my (%args) = @_;
226 0           $args{req_method} = DELETE;
227              
228 0           return $self->_request(%args);
229             }
230              
231             sub _PUT {
232 0     0     my $self = shift;
233 0           my (%args) = @_;
234 0           $args{req_method} = PUT;
235              
236 0           return $self->_request(%args);
237             }
238              
239             sub _decode {
240 0     0     my ($self, $type, $json, $key) = @_;
241 0 0         my $attrs = $key ? $json->{$key} : $json;
242 0           $attrs->{DigitalOcean} = $self;
243 0           return $type->new($attrs);
244             }
245              
246             sub _decode_many {
247 0     0     my ($self, $type, $arr) = @_;
248 0           [map { $self->_decode($type, $_) } @{$arr}];
  0            
  0            
249             }
250              
251              
252             sub get_user_information {
253 0     0 1   my ($self) = @_;
254              
255 0           my $do_response = $self->_GET(path => "account");
256 0           return $self->_decode('DigitalOcean::Account', $do_response->json, 'account');
257             }
258              
259             sub _get_collection {
260 0     0     my ($self, $path, $type_name, $json_key, $params, $init_objects) = @_;
261              
262 0 0         $init_objects = [] unless $init_objects;
263              
264 0           my $do_response = $self->_GET(path => $path, params => $params);
265              
266 0           return DigitalOcean::Collection->new (
267             DigitalOcean => $self,
268             type_name => $type_name,
269             json_key => $json_key,
270             params => $params,
271             response => $do_response,
272             init_objects => $init_objects,
273             );
274             }
275              
276             sub _get_object {
277 0     0     my ($self, $path, $type_name, $json_key) = @_;
278              
279 0           my $do_response = $self->_GET(path => $path);
280 0           return $self->_decode($type_name, $do_response->json, $json_key);
281             }
282              
283             sub _get_array {
284 0     0     my ($self, $path, $type_name, $json_key) = @_;
285              
286 0           my $do_response = $self->_GET(path => $path);
287              
288 0           my $arr;
289 0 0         if($json_key) {
290 0           $arr = $do_response->json->{$json_key};
291             }
292             else {
293 0           $arr = $do_response->json;
294             }
295              
296 0           return $self->_decode_many($type_name, $arr);
297             }
298              
299             sub _put_object {
300 0     0     my ($self, $path, $type_name, $json_key, $req_body_hash) = @_;
301              
302 0           my $do_response = $self->_PUT(path => $path, req_body_hash => $req_body_hash);
303 0           return $self->_decode($type_name, $do_response->json, $json_key);
304             }
305              
306             sub _post_object {
307 0     0     my ($self, $path, $type_name, $json_key, $req_body_hash) = @_;
308              
309 0           my $do_response = $self->_POST(path => $path, req_body_hash => $req_body_hash);
310 0           return $self->_decode($type_name, $do_response->json, $json_key);
311             }
312              
313             sub _create {
314 0     0     my ($self, $path, $type_name, $json_key, $req_body_hash) = @_;
315              
316 0           my $do_response = $self->_POST(path => $path, req_body_hash => $req_body_hash);
317 0           return $self->_decode($type_name, $do_response->json, $json_key);
318             }
319              
320             sub _delete {
321 0     0     my $self = shift;
322 0           my (%args) = @_;
323 0           my $do_response = $self->_DELETE(%args);
324              
325 0           return $do_response->status_code == 204;
326             }
327              
328             sub _action {
329 0     0     my $self = shift;
330 0           my (%args) = @_;
331              
332             #don't delete, because _request might need to wait on event
333 0           my $wait_on_action = $args{req_body_hash}->{wait_on_action};
334              
335 0           my $do_response = $self->_POST(%args);
336              
337 0           my $action = $self->_decode('DigitalOcean::Action', $do_response->json, 'action');
338              
339 0 0 0       $action->wait if $wait_on_action or $self->wait_on_actions;
340              
341 0           return $action;
342             }
343              
344              
345             sub actions {
346 0     0 1   my ($self, $per_page) = @_;
347 0           my $init_arr = [['DigitalOcean', $self]];
348 0           return $self->_get_collection('actions', 'DigitalOcean::Action', 'actions', {per_page => $per_page}, $init_arr);
349             }
350              
351              
352             sub action {
353 0     0 1   my ($self, $id) = @_;
354              
355 0           return $self->_get_object("actions/$id", 'DigitalOcean::Action', 'action');
356             }
357              
358              
359             sub domains {
360 0     0 1   my ($self, $per_page) = @_;
361 0           my $init_arr = [['DigitalOcean', $self]];
362 0           return $self->_get_collection('domains', 'DigitalOcean::Domain', 'domains', {per_page => $per_page}, $init_arr);
363             }
364              
365            
366             sub create_domain {
367 0     0 1   my $self = shift;
368 0           my %args = @_;
369              
370 0           my $domain = $self->_create('domains', 'DigitalOcean::Domain', 'domain', \%args);
371 0           $domain->DigitalOcean($self);
372              
373 0           return $domain;
374             }
375              
376              
377             sub domain {
378 0     0 1   my ($self, $id) = @_;
379              
380 0           my $domain = $self->_get_object("domains/$id", 'DigitalOcean::Domain', 'domain');
381 0           $domain->DigitalOcean($self);
382              
383 0           return $domain;
384             }
385              
386              
387             sub create_droplet {
388 0     0 1   my $self = shift;
389 0           my %args = @_;
390              
391 0           my $droplet = $self->_create('droplets', 'DigitalOcean::Droplet', 'droplet', \%args);
392 0           $droplet->DigitalOcean($self);
393              
394 0           return $droplet;
395             }
396              
397              
398             sub droplet {
399 0     0 1   my ($self, $id) = @_;
400              
401 0           my $droplet = $self->_get_object("droplets/$id", 'DigitalOcean::Droplet', 'droplet');
402 0           $droplet->image->DigitalOcean($self);
403              
404 0           return $droplet;
405             }
406              
407              
408             sub droplets {
409 0     0 1   my ($self, $per_page) = @_;
410 0           my $init_arr = [['DigitalOcean', $self]];
411 0           return $self->_get_collection('droplets', 'DigitalOcean::Droplet', 'droplets', {per_page => $per_page}, $init_arr);
412             }
413              
414              
415             sub droplet_upgrades {
416 0     0 1   my ($self) = @_;
417              
418 0           return $self->_get_array('droplet_upgrades', 'DigitalOcean::Droplet::Upgrade');
419             }
420              
421             sub _images {
422 0     0     my ($self, $params) = @_;
423 0           my $init_arr = [['DigitalOcean', $self]];
424 0           return $self->_get_collection('images', 'DigitalOcean::Image', 'images', $params, $init_arr);
425             }
426              
427              
428             sub images {
429 0     0 1   my ($self, $per_page) = @_;
430 0           return $self->_images({per_page => $per_page});
431             }
432              
433              
434             sub distribution_images {
435 0     0 1   my ($self, $per_page) = @_;
436 0           return $self->_images({per_page => $per_page, type => 'distribution'});
437             }
438              
439              
440             sub application_images {
441 0     0 1   my ($self, $per_page) = @_;
442 0           return $self->_images({per_page => $per_page, type => 'application'});
443             }
444              
445              
446             sub user_images {
447 0     0 1   my ($self, $per_page) = @_;
448 0           return $self->_images({per_page => $per_page, private => 'true'});
449             }
450              
451              
452             sub image {
453 0     0 1   my ($self, $id_or_slug) = @_;
454              
455 0           my $image = $self->_get_object("images/$id_or_slug", 'DigitalOcean::Image', 'image');
456              
457 0           return $image;
458             }
459              
460              
461             sub ssh_keys {
462 0     0 1   my ($self, $per_page) = @_;
463 0           my $init_arr = [['DigitalOcean', $self]];
464 0           return $self->_get_collection('account/keys', 'DigitalOcean::SSH::Key', 'ssh_keys', {per_page => $per_page}, $init_arr);
465             }
466              
467            
468             sub create_ssh_key {
469 0     0 1   my $self = shift;
470 0           my %args = @_;
471              
472 0           my $ssh_key = $self->_create('account/keys', 'DigitalOcean::SSH::Key', 'ssh_key', \%args);
473 0           $ssh_key->DigitalOcean($self);
474              
475 0           return $ssh_key;
476             }
477              
478              
479             sub ssh_key {
480 0     0 1   my ($self, $id_or_finger) = @_;
481              
482 0           my $ssh_key = $self->_get_object("account/keys/$id_or_finger", 'DigitalOcean::SSH::Key', 'ssh_key');
483              
484 0           return $ssh_key;
485             }
486              
487              
488             sub regions {
489 0     0 1   my ($self, $per_page) = @_;
490 0           return $self->_get_collection('regions', 'DigitalOcean::Region', 'regions', {per_page => $per_page});
491             }
492              
493              
494             sub sizes {
495 0     0 1   my ($self, $per_page) = @_;
496 0           return $self->_get_collection('sizes', 'DigitalOcean::Size', 'sizes', {per_page => $per_page});
497             }
498              
499             __PACKAGE__->meta->make_immutable();
500              
501             1;
502              
503             __END__