File Coverage

blib/lib/WebService/UrbanAirship/APNS.pm
Criterion Covered Total %
statement 50 203 24.6
branch 2 90 2.2
condition 1 28 3.5
subroutine 15 25 60.0
pod 8 9 88.8
total 76 355 21.4


line stmt bran cond sub pod time code
1             package WebService::UrbanAirship::APNS;
2              
3 4     4   4606 use 5.006;
  4         15  
  4         163  
4              
5 4     4   23 use strict;
  4         6  
  4         160  
6 4     4   40 use warnings FATAL => qw(all);
  4         7  
  4         177  
7              
8 4     4   2587 use WebService::UrbanAirship;
  4         12  
  4         137  
9              
10 4     4   4998 use JSON::XS ();
  4         33286  
  4         108  
11 4     4   3501 use HTTP::Request ();
  4         99386  
  4         129  
12 4     4   12551 use HTTP::Response ();
  4         32753  
  4         111  
13 4     4   35 use HTTP::Headers ();
  4         9  
  4         66  
14 4     4   7292 use LWP::UserAgent ();
  4         93393  
  4         115  
15 4     4   4505 use LWP::Protocol::https ();
  4         498985  
  4         9215  
16              
17              
18             #---------------------------------------------------------------------
19             # globals
20             #---------------------------------------------------------------------
21              
22             our $DEBUG = 0;
23              
24             our $VERSION = "0.02";
25              
26             our @ISA = qw(WebService::UrbanAirship);
27              
28              
29             #---------------------------------------------------------------------
30             # constructor
31             #---------------------------------------------------------------------
32             sub new {
33              
34 8     8 1 14809 my $class = shift;
35              
36 8         48 my %args = @_;
37              
38 8         21 foreach my $key (qw(application_key application_secret application_push_secret)) {
39 21 100       106 die "missing argument: $key"
40             unless $args{$key};
41             }
42              
43 5         38 my $self = {_push_secret => delete $args{application_push_secret},
44             _secret => delete $args{application_secret},
45             _key => delete $args{application_key},
46             };
47              
48 5         18 bless $self, $class;
49              
50 5         31 $self->_init(%args);
51              
52 5         240 return $self;
53             }
54              
55              
56             #---------------------------------------------------------------------
57             # private initialization routine
58             #---------------------------------------------------------------------
59             sub _init {
60              
61 2     2   15 shift->ua;
62             }
63              
64              
65             #---------------------------------------------------------------------
66             # set up the ua object
67             #---------------------------------------------------------------------
68             sub ua {
69              
70 2     2 0 5 my $self = shift;
71              
72 2         5 my %args = @_;
73              
74 2   33     77 my $ua = $self->{_ua} ||
75             LWP::UserAgent->new(agent => $self->_agent(),
76             protocols_allowed => [ qw(https) ],
77             timeout => $self->_timeout(),
78             );
79              
80             # set (or reset) the headers
81 2         16540 my $headers = HTTP::Headers->new();
82              
83             # all data needs to be JSON
84 2         31 $headers->content_type('application/json');
85              
86             # set the authentication headers here
87 2         36 $headers->authorization_basic($self->{_key}, $self->{_push_secret});
88              
89 2         7665 $ua->default_headers($headers);
90              
91 2         64 $self->{_ua} = $ua;
92              
93 2         7 return $ua;
94             }
95              
96              
97              
98             #---------------------------------------------------------------------
99             # default user-agent string
100             #---------------------------------------------------------------------
101             sub _agent {
102              
103 2     2   19 return join '/', __PACKAGE__, $VERSION;
104             }
105              
106             #---------------------------------------------------------------------
107             # default timeout
108             #---------------------------------------------------------------------
109             sub _timeout {
110              
111 2     2   21 return 60;
112             }
113              
114              
115             #---------------------------------------------------------------------
116             # main api...
117             #---------------------------------------------------------------------
118              
119             #---------------------------------------------------------------------
120             # device registration
121             #---------------------------------------------------------------------
122              
123             sub register_device {
124              
125 0     0 1   my $self = shift;
126              
127 0           my %args = @_;
128              
129 0           my $token = delete $args{device_token};
130              
131 0 0         return unless $token;
132              
133             # as a shortcut, tidy the id per urban airship specs...
134            
135 0           $token = uc $token;
136 0           $token =~ s/[-\s<>]//g;
137              
138 0           my $json;
139              
140 0 0         if (scalar keys %args) {
141              
142 0 0         delete $args{alias} unless $args{alias};
143 0 0 0       delete $args{tags} unless $args{tags} && ref $args{tags};
144              
145 0           $json = JSON::XS::encode_json(\%args);
146              
147             }
148              
149 0           my $ua = $self->ua;
150              
151 0           my $headers = $ua->default_headers;
152              
153             # this API requires the secret key, not the push secret key
154 0           $headers->authorization_basic($self->{_key}, $self->{_secret});
155              
156 0           my $uri = $self->_api_uri;
157              
158 0           $uri->path(join '/', '/api/device_tokens', $token);
159              
160 0           my $request = HTTP::Request->new('PUT',
161             $uri,
162             $headers,
163             $json);
164              
165 0           return $self->_request($request);
166             }
167              
168              
169             sub ping_device {
170              
171 0     0 1   my $self = shift;
172              
173 0           my %args = @_;
174              
175 0           my $token = delete $args{device_token};
176              
177 0 0         return unless $token;
178              
179             # as a shortcut, tidy the id per urban airship specs...
180              
181 0           $token = uc $token;
182 0           $token =~ s/[-\s<>]//g;
183              
184 0           my $ua = $self->ua;
185              
186 0           my $headers = $ua->default_headers;
187              
188             # this API requires the secret key, not the push secret key
189 0           $headers->authorization_basic($self->{_key}, $self->{_secret});
190              
191 0           my $uri = $self->_api_uri;
192              
193 0           $uri->path(join '/', '/api/device_tokens', $token);
194              
195 0           my $request = HTTP::Request->new('GET',
196             $uri,
197             $headers);
198              
199 0           return $self->_request($request, 1);
200             }
201              
202              
203             sub push {
204              
205 0     0 1   my $self = shift;
206              
207 0           my %args = @_;
208              
209 0           my ($perl, $body) = $self->_craft_single_push(\%args);
210              
211 0 0         return unless $perl;
212              
213 0           my $json = JSON::XS::encode_json($perl);
214              
215 0           my $uri = $self->_api_uri;
216              
217 0           $uri->path('/api/push/');
218              
219 0           my $request = HTTP::Request->new('POST',
220             $uri);
221              
222 0           $request->content($json);
223              
224 0           return $self->_request($request, $body);
225             }
226              
227             sub batch {
228              
229 0     0 1   my $self = shift;
230              
231 0           my @args = @_;
232              
233 0           my @array;
234              
235 0           foreach my $key (@args) {
236              
237 0 0         next unless ref $key eq 'HASH';
238            
239 0           my ($perl) = $self->_craft_single_push($key, 1);
240              
241 0 0         next unless $perl;
242              
243 0           CORE::push @array, $perl;
244             }
245              
246 0 0         return unless scalar @array;
247              
248 0           my $json = JSON::XS::encode_json(\@array);
249              
250 0           my $uri = $self->_api_uri;
251              
252 0           $uri->path('/api/push/batch/');
253              
254 0           my $request = HTTP::Request->new('POST',
255             $uri);
256              
257 0           $request->content($json);
258              
259 0           return $self->_request($request);
260             }
261              
262             sub broadcast {
263              
264 0     0 1   my $self = shift;
265              
266 0           my %args = @_;
267              
268 0           my $payload = $self->_craft_payload(\%args);
269              
270 0 0         return unless $payload;
271              
272 0           my $perl = {aps => $payload};
273              
274 0 0         if (my $exclude = delete $args{exclude_tokens}) {
275 0 0 0       $perl->{exclude_tokens} = $exclude
276             if ref $exclude && ref $exclude eq 'ARRAY';
277             }
278              
279 0           my $json = JSON::XS::encode_json($perl);
280              
281 0           my $uri = $self->_api_uri;
282              
283 0           $uri->path('/api/push/broadcast/');
284              
285 0           my $request = HTTP::Request->new('POST',
286             $uri);
287              
288 0           $request->content($json);
289              
290 0           return $self->_request($request);
291             }
292              
293             sub feedback {
294              
295 0     0 1   my $self = shift;
296              
297 0           my %args = @_;
298              
299 0           my $date = delete $args{since};
300              
301 0 0         return unless $date;
302              
303 0           my $uri = $self->_api_uri;
304              
305 0           $uri->path('/api/device_tokens/feedback/');
306              
307 0           $uri->query(join '=', 'since', $date);
308              
309 0           my $request = HTTP::Request->new('GET',
310             $uri);
311              
312 0           return $self->_request($request, 1);
313             }
314              
315             sub stats {
316              
317 0     0 1   my $self = shift;
318              
319 0           my %args = @_;
320              
321 0           my $start = delete $args{start};
322 0           my $end = delete $args{end};
323              
324 0           my $format = delete $args{format};
325              
326 0 0 0       return unless $start && $end;
327              
328 0           my $uri = $self->_api_uri;
329              
330 0           $uri->path('/api/push/stats/');
331              
332 0           my $query = "start=$start&end=$end";
333              
334 0 0         $query = join '&', $query, "format=$format" if $format;
335              
336 0           $uri->query($query);
337              
338 0           my $request = HTTP::Request->new('GET',
339             $uri);
340              
341 0           return $self->_request($request, 1);
342             }
343              
344              
345             sub _request {
346              
347 0     0     my $self = shift;
348              
349 0           my $request = shift;
350              
351 0           my $body = shift;
352              
353 0 0         if ($request->method eq 'GET') {
354 0           $request->headers->remove_header('content-type');
355             }
356              
357 0 0         print STDERR "request: ", $request->as_string
358             if $DEBUG;
359              
360 0           my $response = $self->ua->request($request);
361              
362 0 0         print STDERR "response: ", $response->as_string
363             if $DEBUG;
364              
365 0 0         if ($response->is_success) {
366              
367 0 0         if ($body) {
368 0           return $response->content;
369             }
370             else {
371 0           return $response->code;
372             }
373             }
374              
375 0           return;
376             }
377              
378              
379             sub _craft_single_push {
380              
381 0     0     my $self = shift;
382              
383 0 0         my %args = %{shift || {}};
  0            
384              
385 0           my $batch = shift;
386              
387 0           my $payload = $self->_craft_payload(\%args);
388              
389 0 0         return unless $payload;
390              
391 0   0       my $tokens = delete $args{device_tokens} || [];
392              
393 0 0         if ($tokens) {
394 0 0         return unless ref $tokens eq 'ARRAY';
395             }
396              
397 0   0       my $aliases = delete $args{aliases} || [];
398              
399 0 0         if ($aliases) {
400 0 0         return unless ref $aliases eq 'ARRAY';
401             }
402              
403 0           my $tags = [];
404              
405 0 0         unless ($batch) {
406 0   0       $tags = delete $args{tags} || [];
407              
408 0 0         if ($tags) {
409 0 0         return unless ref $tags eq 'ARRAY';
410             }
411             }
412              
413 0 0 0       return unless (scalar @$aliases || scalar @$tokens || scalar @$tags);
      0        
414              
415 0           my $perl = {};
416              
417 0 0         if (scalar @$aliases) {
418 0           $perl->{aliases} = $aliases;
419             }
420              
421 0 0         if (scalar @$tokens) {
422 0           $perl->{device_tokens} = $tokens;
423             }
424              
425 0 0         if (scalar @$tags) {
426 0           $perl->{tags} = $tags;
427             }
428              
429 0 0         return unless scalar keys %$perl;
430              
431 0           $perl->{aps} = $payload;
432              
433 0           my $body = 0;
434              
435 0 0         unless ($batch) {
436 0 0         if (my $schedule = delete $args{schedule_for}) {
437 0 0 0       if (ref $schedule && ref $schedule eq 'ARRAY') {
438 0           $perl->{schedule_for} = $schedule ;
439 0           $body = 1;
440             }
441             }
442              
443 0 0         if (my $exclude = delete $args{exclude_tokens}) {
444 0 0 0       $perl->{exclude_tokens} = $exclude
445             if ref $exclude && ref $exclude eq 'ARRAY';
446             }
447             }
448              
449 0           return ($perl, $body);
450             }
451              
452              
453             sub _craft_payload {
454              
455 0     0     my $self = shift;
456              
457 0 0         my %args = %{shift || {}};
  0            
458              
459 0           my $badge = eval { int delete $args{badge} };
  0            
460 0           my $alert = delete $args{alert};
461 0           my $sound = delete $args{sound};
462              
463 0           my $payload = {};
464              
465 0 0         $payload->{badge} = $badge if defined $badge;
466 0 0         $payload->{sound} = $sound if $sound;
467 0 0         $payload->{alert} = $alert if $alert;
468              
469 0 0         return unless scalar keys %$payload;
470              
471 0           return $payload;
472             }
473              
474              
475             __END__