File Coverage

blib/lib/Protocol/Notifo.pm
Criterion Covered Total %
statement 80 80 100.0
branch 17 18 94.4
condition 2 3 66.6
subroutine 16 16 100.0
pod 4 4 100.0
total 119 121 98.3


line stmt bran cond sub pod time code
1             package Protocol::Notifo;
2             BEGIN {
3 3     3   135645 $Protocol::Notifo::VERSION = '0.004';
4             }
5              
6             # ABSTRACT: utilities to build requests for the notifo.com service
7              
8 3     3   26 use strict;
  3         7  
  3         98  
9 3     3   17 use warnings;
  3         6  
  3         240  
10 3     3   15 use Carp 'confess';
  3         5  
  3         151  
11 3     3   5166 use JSON 'decode_json';
  3         52791  
  3         20  
12 3     3   3890 use MIME::Base64 'encode_base64';
  3         2516  
  3         222  
13 3     3   2737 use File::HomeDir;
  3         21496  
  3         226  
14 3     3   2822 use File::Spec::Functions qw( catfile );
  3         3120  
  3         211  
15 3     3   2865 use URI ();
  3         16175  
  3         72  
16 3     3   2850 use namespace::clean;
  3         62816  
  3         63  
17              
18             sub new {
19 8     8 1 7060 my ($class, %args) = @_;
20 8         31 my $self = bless $class->_read_config_file, $class;
21              
22 7         23 for my $f (qw( user api_key )) {
23 13 100       60 $self->{$f} = $args{$f} if exists $args{$f};
24 13 100       88 confess("Missing required parameter '$f' to new(), ") unless $self->{$f};
25             }
26              
27 5         14 $self->{base_url} = 'https://api.notifo.com/v1';
28 5         58 $self->{auth_hdr} = encode_base64(join(':', @$self{qw(user api_key)}), '');
29              
30 5         102 return $self;
31             }
32              
33              
34             sub parse_response {
35 4     4 1 21626 my ($self, %args) = @_;
36              
37 4         7 my $res = {};
38 4         7 eval { $res = decode_json(delete $args{http_body}) };
  4         51  
39 4 100       15 if ($@) {
40 1         3 $res->{status} = 'error';
41 1         3 $res->{response_code} = -1;
42 1         2 $res->{response_message} = $args{http_status_line};
43             }
44              
45 4         13 for my $k (qw( http_response_code http_status_line)) {
46 8         21 $res->{$k} = delete $args{$k};
47             }
48 4         8 $res->{other} = \%args;
49              
50 4         18 return $res;
51             }
52              
53              
54             sub send_notification {
55 7     7 1 63552 my ($self, %args) = @_;
56              
57 7         64 my %call = (
58             url => "$self->{base_url}/send_notification",
59             method => 'POST',
60             headers => [Authorization => "Basic $self->{auth_hdr}"],
61             args => {},
62             );
63              
64 7         24 for my $f (qw( to msg label title uri )) {
65 35         46 my $v = $args{$f};
66 35 100       78 next unless defined $v;
67              
68 16         34 $call{args}{$f} = $v;
69             }
70              
71 7 100       51 confess("Missing required argument 'msg', ") unless $call{args}{msg};
72              
73 6         23 _build_http_request(\%call);
74              
75 6         227 return \%call;
76             }
77              
78             sub config_file {
79 11     11 1 24072 my ($self) = @_;
80              
81 11   66     102 return $ENV{NOTIFO_CFG} || catfile(File::HomeDir->my_home, '.notifo.rc');
82             }
83              
84              
85             sub _read_config_file {
86 8     8   17 my ($self) = @_;
87 8         12 my %opts;
88              
89 8         28 my $fn = $self->config_file;
90 8 100       418 return \%opts unless -r $fn;
91              
92 3 50       144 open(my $fh, '<', $fn) || confess("Could not open file '$fn': $!, ");
93              
94 3         90 while (my $l = <$fh>) {
95 17         27 chomp($l);
96 17         107 $l =~ s/^\s*(#.*)?|\s*$//g;
97 17 100       58 next unless $l;
98              
99 7         35 my ($k, $v) = $l =~ m/(\S+)\s*[=:]\s*(.*)/;
100 7 100       42 confess("Could not parse line $. of $fn ('$l'), ") unless $k;
101              
102 6         31 $opts{$k} = $v;
103             }
104              
105 2         51 return \%opts;
106             }
107              
108             sub _build_http_request {
109 6     6   11 my ($req) = @_;
110 6         23 my ($meth, $url, $args, $hdrs) = @$req{qw(method url args headers)};
111              
112 6         40 my $uri = $req->{url} = URI->new($url);
113 6         12522 $uri->query_form($args);
114              
115 6         734 $req->{body} = $uri->query;
116 6         58 push @$hdrs, 'Content-Type' => 'application/x-www-form-urlencoded';
117 6         19 push @$hdrs, 'Content-Length' => length($req->{body});
118              
119 6         19 $uri->query_form([]);
120             }
121              
122             1;
123              
124              
125              
126             =pod
127              
128             =head1 NAME
129              
130             Protocol::Notifo - utilities to build requests for the notifo.com service
131              
132             =head1 VERSION
133              
134             version 0.004
135              
136             =head1 SYNOPSIS
137              
138             ## Reads user and api_key from configuration file
139             my $pn = Protocol::Notifo->new;
140            
141             ## Use a particular user and api_key, overrides configuration file
142             my $pn = Protocol::Notifo->new(user => 'me', api_key => 'my_key');
143            
144             my $req = $pn->send_notification(msg => 'Hi!');
145            
146             .... send $req, get a response back ....
147            
148             my $res = $pn->parse_response($response_http_code, $response_body);
149            
150             .... do stuff with $res ....
151              
152             =head1 DESCRIPTION
153              
154             This module provides an API to prepare requests to the
155             L service.
156              
157             The module doesn't actually execute the HTTP request. It only prepares
158             all the information required for such request to be performed. As such
159             this module is not to be used by end users, but by writters of
160             notifo.com API clients.
161              
162             If you are an end-user and want to call the API, you should look into
163             the modules L and L.
164              
165             This module supports both the User API and the Service API.
166             Differences between the behaviour of the two are noted in this
167             documentation where relevant.
168              
169             You need a notifo.com account to be able to use this module. The account
170             will give you access to an API username, and an API key. Both are required
171             arguments of our L.
172              
173             The module also supports a configuration file. See
174             L to learn which configuration file will
175             be loaded automatically, if found.
176              
177             For all the details of the notifo.com API, check out the site
178             L.
179              
180             =head1 CONSTRUCTORS
181              
182             =head2 new
183              
184             Creates new C object.
185              
186             It first tries to load default values from a configuration file. If you
187             set the environment variable C, it will try that. If not, it
188             will default to L<< File::HomeDir->my_home()|File::HomeDir/my_home >>. See
189             section L for the format of those files.
190              
191             You can also pass a hash of options, that will override the defaults set
192             by the configuration file. The following options are accepted:
193              
194             =over 4
195              
196             =item user
197              
198             The API username.
199              
200             =item api_key
201              
202             The API key.
203              
204             =back
205              
206             Values for this two options can be found in the
207             L
208             of the L.
209              
210             =head1 METHODS
211              
212             =head2 parse_response
213              
214             Accepts a hash with response information. The following fields must be present:
215              
216             =over 4
217              
218             =item http_response_code
219              
220             The HTTP code of the response message.
221              
222             =item http_status_line
223              
224             The HTTP status line of the response message.
225              
226             =item http_body
227              
228             The response content.
229              
230             =back
231              
232             Other fields might be passed, they will be ignored and returned in the
233             C field of the return value.
234              
235             This method parses the content, adds the HTTP response code and returns a hashref
236             with all the fields.
237              
238             The following fields are present on all responses:
239              
240             =over 4
241              
242             =item status
243              
244             A string, either C or C.
245              
246             =item http_response_code
247              
248             The HTTP code of the response message.
249              
250             =item http_status_line
251              
252             The HTTP status line of the response message.
253              
254             =item response_code
255              
256             A notifo.com integer response code.
257              
258             =item response_message
259              
260             A text description of the response. Specially useful when C
261             is C.
262              
263             =item other
264              
265             All C other parameters.
266              
267             =back
268              
269             =head2 send_notification
270              
271             Prepares a request for the
272             L
273             API.
274              
275             Accepts a hash with options. The following options are supported:
276              
277             =over 4
278              
279             =item msg
280              
281             The notification message. This parameter is B.
282              
283             =item to
284              
285             The destination user. If the API username/key pair used is of a User
286             account, then this parameter is ignored and can be safelly ommited.
287              
288             A User account can only send notifications to itself. A Service account
289             can send notifications to all his subscribed users.
290              
291             =item label
292              
293             A label describing the application that is sending the
294             notification. With Service accounts, this option is ignored and the
295             Service Name is used.
296              
297             =item title
298              
299             The title or subject of the notification.
300              
301             =item uri
302              
303             The URL for the event. On some clients you can click the notification and jump to this URL.
304              
305             =back
306              
307             The return value is a hashref with all the relevant information to
308             perform the HTTP request: the url and the method to use, the
309             Authorization header, and the query form fields.
310              
311             An example:
312              
313             url => URI->new("https://api.notifo.com/v1/send_notification"),
314             method => "POST",
315             args => {
316             label => "l",
317             msg => "hello",
318             title => "t",
319             to => "to"
320             },
321             headers => [
322             "Authorization" => "Basic bWU6bXlfa2V5",
323             "Content-Type" => "application/x-www-form-urlencoded",
324             "Content-Length" => 31,
325             ],
326             body => "msg=hello&to=to&title=t&label=l",
327              
328             The following keys are always present in the hashref:
329              
330             =over 4
331              
332             =item url
333              
334             The L object representing the URL where the HTTP request should
335             be sent to.
336              
337             =item method
338              
339             The HTTP method to use.
340              
341             =item args
342              
343             A hashref with all the URL query form fields and values.
344              
345             =item headers
346              
347             A hashref with all the headers to include in the HTTP request.
348              
349             =item body
350              
351             The C in C form, can be used as
352             the body of the HTTP request.
353              
354             =back
355              
356             =head2 config_file
357              
358             Returns the configuration file that this module will attempt to use.
359              
360             =head1 CONFIGURATION FILE
361              
362             The configuration file is line based. Empty lines os just spaces/tabs,
363             or lines starting with # are ignored.
364              
365             All other lines are parsed for commands, in the form
366             C. The C can be a C<=> or a C<:>.
367              
368             See the L for the commands you can use,
369             they are the same ones as the accepted options.
370              
371             =head1 TODO
372              
373             Future versions of this module will implement the other APIs:
374              
375             =over 4
376              
377             =item subscribe_user
378              
379             =item send_message
380              
381             =back
382              
383             Patches welcome.
384              
385             =head1 AUTHOR
386              
387             Pedro Melo
388              
389             =head1 COPYRIGHT AND LICENSE
390              
391             This software is Copyright (c) 2010 by Pedro Melo.
392              
393             This is free software, licensed under:
394              
395             The Artistic License 2.0
396              
397             =cut
398              
399              
400             __END__