File Coverage

blib/lib/Net/Heroku.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Net::Heroku;
2 1     1   23489 use Mojo::Base -base;
  0            
  0            
3             use Net::Heroku::UserAgent;
4             use Mojo::JSON;
5             use Mojo::Util 'url_escape';
6              
7             our $VERSION = 0.10;
8              
9             has host => 'api.heroku.com';
10             has ua => sub { Net::Heroku::UserAgent->new(host => shift->host) };
11             has 'api_key';
12              
13             sub new {
14             my $self = shift->SUPER::new(@_);
15             my %params = @_;
16              
17             # Assume email & pass
18             $self->ua->api_key(
19             defined $params{email}
20             ? $self->_retrieve_api_key(@params{qw/ email password /})
21             : $params{api_key} ? $params{api_key}
22             : ''
23             );
24              
25             return $self;
26             }
27              
28             sub error {
29             my $self = shift;
30             my $res = $self->ua->tx->res;
31              
32             return if $res->code =~ /^2\d{2}$/;
33              
34             return (
35             code => $res->code,
36             message => ($res->json ? $res->json->{error} : $res->body)
37             );
38             }
39              
40             sub _retrieve_api_key {
41             my ($self, $email, $password) = @_;
42              
43             return $self->ua->post(
44             '/login' => form => {email => $email, password => $password})
45             ->res->json('/api_key');
46             }
47              
48             sub apps {
49             my ($self, $name) = @_;
50              
51             return @{$self->ua->get('/apps')->res->json || []};
52             }
53              
54             sub app_created {
55             my ($self, %params) = (shift, @_);
56              
57             return 1
58             if $self->ua->put('/apps/' . $params{name} . '/status')->res->code == 201;
59             }
60              
61             sub destroy {
62             my ($self, %params) = @_;
63              
64             my $res = $self->ua->delete('/apps/' . $params{name})->res;
65             return 1 if $res->{code} == 200;
66             }
67              
68             sub create {
69             my ($self, %params) = (shift, @_);
70              
71             # Empty space names no longer allowed
72             #delete $params{name} if !$params{name};
73              
74             my @ar = map +("app[$_]" => $params{$_}) => keys %params;
75             %params = (
76             'app[stack]' => 'cedar',
77             @ar,
78             );
79              
80             my $res = $self->ua->post('/apps' => form => \%params)->res;
81              
82             return $res->json && $res->code == 202 ? %{$res->json} : ();
83             }
84              
85             sub add_config {
86             my ($self, %params) = (shift, @_);
87              
88             return %{$self->ua->put(
89             '/apps/'
90             . (defined $params{name} and delete($params{name}))
91             . '/config_vars' => Mojo::JSON->new->encode(\%params)
92             )->res->json
93             || {}
94             };
95             }
96              
97             sub config {
98             my ($self, %params) = (shift, @_);
99              
100             return
101             %{$self->ua->get('/apps/' . $params{name} . '/config_vars')->res->json
102             || []};
103             }
104              
105             sub add_key {
106             my ($self, %params) = (shift, @_);
107              
108             return 1
109             if $self->ua->post('/user/keys' => $params{key})->res->{code} == 200;
110             }
111              
112             sub keys {
113             my ($self, %params) = (shift, @_);
114              
115             return @{$self->ua->get('/user/keys')->res->json || []};
116             }
117              
118             sub remove_key {
119             my ($self, %params) = (shift, @_);
120              
121             my $res =
122             $self->ua->delete('/user/keys/' . url_escape($params{key_name}))->res;
123             return 1 if $res->{code} == 200;
124             }
125              
126             sub ps {
127             my ($self, %params) = (shift, @_);
128              
129             return @{$self->ua->get('/apps/' . $params{name} . '/ps')->res->json || []};
130             }
131              
132             sub run {
133             my ($self, %params) = (shift, @_);
134              
135             return
136             %{$self->ua->post('/apps/' . $params{name} . '/ps' => form => \%params)
137             ->res->json || {}};
138             }
139              
140             sub restart {
141             my ($self, %params) = (shift, @_);
142              
143             return 1
144             if $self->ua->post(
145             '/apps/' . $params{name} . '/ps/restart' => form => \%params)->res->code
146             == 200;
147             }
148              
149             sub stop {
150             my ($self, %params) = (shift, @_);
151              
152             return 1
153             if $self->ua->post(
154             '/apps/' . $params{name} . '/ps/stop' => form => \%params)->res->code
155             == 200;
156             }
157              
158             sub releases {
159             my ($self, %params) = (shift, @_);
160              
161             my $url =
162             '/apps/'
163             . $params{name}
164             . '/releases'
165             . ($params{release} ? '/' . $params{release} : '');
166              
167             my $releases = $self->ua->get($url)->res->json || [];
168              
169             return $params{release} ? %$releases : @$releases;
170             }
171              
172             sub rollback {
173             my ($self, %params) = (shift, @_);
174              
175             $params{rollback} = delete $params{release};
176              
177             return $params{rollback}
178             if $self->ua->post(
179             '/apps/' . $params{name} . '/releases' => form => \%params)->res->code
180             == 200;
181             }
182              
183             sub add_domain {
184             my ($self, %params) = (shift, @_);
185              
186             my $url = '/apps/' . $params{name} . '/domains';
187              
188             return 1
189             if $self->ua->post(
190             $url => form => {'domain_name[domain]' => $params{domain}})->res->code
191             == 200;
192             }
193              
194             sub domains {
195             my ($self, %params) = (shift, @_);
196              
197             my $url = '/apps/' . $params{name} . '/domains';
198              
199             return @{$self->ua->get($url)->res->json || []};
200             }
201              
202             sub remove_domain {
203             my ($self, %params) = (shift, @_);
204              
205             return 1
206             if $self->ua->delete(
207             '/apps/' . $params{name} . '/domains/' . url_escape($params{domain}))
208             ->res->code == 200;
209             }
210              
211             1;
212              
213             =head1 NAME
214              
215             Net::Heroku - Heroku API
216              
217             =head1 DESCRIPTION
218              
219             Heroku API
220              
221             Requires Heroku account - free @ L
222              
223             =head1 USAGE
224              
225             my $h = Net::Heroku->new(api_key => api_key);
226             - or -
227             my $h = Net::Heroku->new(email => $email, password => $password);
228              
229             my %res = $h->create;
230              
231             $h->add_config(name => $res{name}, BUILDPACK_URL => ...);
232             $h->restart(name => $res{name});
233              
234             say $_->{name} for $h->apps;
235              
236             $h->destroy(name => $res{name});
237              
238              
239             warn 'Error:' . $h->error # Error: App not found.
240             if not $h->destroy(name => $res{name});
241              
242             if (!$h->destroy(name => $res{name})) {
243             my %err = $h->error;
244             warn "$err{code}, $err{message}"; # 404, App not found.
245             }
246              
247             =head1 METHODS
248              
249             =head2 new
250              
251             my $h = Net::Heroku->new(api_key => $api_key);
252             - or -
253             my $h = Net::Heroku->new(email => $email, password => $password);
254              
255             Requires api key or user/pass. Returns Net::Heroku object.
256              
257             =head2 apps
258              
259             my @apps = $h->apps;
260              
261             Returns list of hash references with app information
262              
263             =head2 destroy
264              
265             my $bool = $h->destroy(name => $name);
266              
267             Requires app name. Destroys app. Returns true if successful.
268              
269             =head2 create
270              
271             my $app = $h->create;
272              
273             Creates a Heroku app. Accepts optional hash list as values, returns hash list. Returns empty list on failure.
274              
275             =head2 add_config
276              
277             my %config = $h->add_config(name => $name, config_key => $config_value);
278              
279             Requires app name. Adds config variables passed in hash list. Returns hash config.
280              
281             =head2 config
282              
283             my %config = $h->config(name => $name);
284              
285             Requires app name. Returns hash reference of config variables.
286              
287             =head2 add_key
288              
289             my $bool = $h->add_key(key => ...);
290              
291             Requires key. Adds ssh public key.
292              
293             =head2 keys
294              
295             my @keys = $h->keys;
296              
297             Returns list of keys
298              
299             =head2 remove_key
300              
301             my $bool = $h->remove_key(key_name => $key_name);
302              
303             Requires name associated with key. Removes key.
304              
305             =head2 ps
306              
307             my @processes = $h->ps(name => $name);
308              
309             Requires app name. Returns list of processes.
310              
311             =head2 run
312              
313             my $process = $h->run(name => $name, command => $command);
314              
315             Requires app name and command. Runs command once. Returns hash response.
316              
317             =head2 restart
318              
319             my $bool = $h->restart(name => $name);
320             my $bool = $h->restart(name => $name, ps => $ps, type => $type);
321              
322             Requires app name. Restarts app. If ps is supplied, only process is restarted.
323              
324             =head2 stop
325              
326             my $bool = $h->stop(name => $name, ps => $ps, type => $type);
327              
328             Requires app name. Stop app process.
329              
330             =head2 releases
331              
332             my @releases = $h->releases(name => $name);
333             my %release = $h->releases(name => $name, release => $release);
334              
335             Requires app name. Returns list of hashrefs.
336             If release name specified, returns hash.
337              
338             =head2 add_domain
339              
340             my $bool = $h->add_domain(name => $name, domain => $domain);
341              
342             Requires app name. Adds domain.
343              
344             =head2 domains
345              
346             my @domains = $h->domains(name => $name);
347              
348             Requires app name. Returns list of hashrefs describing assigned domains.
349              
350             =head2 remove_domain
351              
352             my $bool = $h->remove_domain(name => $name, domain => $domain);
353              
354             Requires app name associated with domain. Removes domain.
355              
356             =head2 rollback
357              
358             my $bool = $h->rollback(name => $name, release => $release);
359              
360             Rolls back to a specified releases
361              
362             =head2 error
363              
364             my $message = $h->error;
365             my %err = $h->error;
366              
367             In scalar context, returns error message from last request
368              
369             In list context, returns hash with keys: code, message.
370              
371             If the last request was successful, returns empty list.
372              
373             =head1 SEE ALSO
374              
375             L, L, L
376              
377             =head1 SOURCE
378              
379             L
380              
381             =head1 VERSION
382              
383             0.10
384              
385             =head1 AUTHOR
386              
387             Glen Hinkle C
388              
389             =cut