File Coverage

blib/lib/DigitalOcean.pm
Criterion Covered Total %
statement 54 221 24.4
branch 0 26 0.0
condition 0 18 0.0
subroutine 18 54 33.3
pod 20 20 100.0
total 92 339 27.1


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