File Coverage

lib/Protocol/Notifo.pm
Criterion Covered Total %
statement 59 59 100.0
branch 14 16 87.5
condition 2 3 66.7
subroutine 13 13 100.0
pod 0 4 0.0
total 88 95 92.6


line stmt bran cond sub pod time code
1             package Protocol::Notifo;
2              
3             # ABSTRACT: utilities to build requests for the notifo.com service
4              
5 3     3   15 use strict;
  3         6  
  3         68  
6 3     3   10 use warnings;
  3         4  
  3         95  
7 3     3   11 use Carp 'confess';
  3         3  
  3         128  
8 3     3   716 use JSON 'decode_json';
  3         32849  
  3         26  
9 3     3   1428 use MIME::Base64 'encode_base64';
  3         1846  
  3         192  
10 3     3   738 use File::HomeDir;
  3         37832  
  3         211  
11 3     3   749 use File::Spec::Functions qw( catfile );
  3         1762  
  3         200  
12 3     3   736 use namespace::clean;
  3         55646  
  3         30  
13              
14             =constructor new
15            
16             Creates new C<Protocol::Notifo> object.
17            
18             It first tries to load default values from a configuration file. If you
19             set the environment variable C<NOTIFO_CFG>, it will try that. If not, it
20             will default to L<File::HomeDir/my_home|File::HomeDir->my_home()>. See
21             L<CONFIGURATION FILE> for the format of the file.
22            
23             You can also pass a hash of options, that will override the
24             configuration file. The following options are accepted:
25            
26             =over 4
27            
28             =item user
29            
30             The API username.
31            
32             =item api_key
33            
34             The API key.
35            
36             =back
37            
38             Values for this two options can be found in the
39             L<http://notifo.com/user/settings|user settings page> of
40             L<http://notifo.com/|Notifo site>.
41            
42             =cut
43             sub new {
44 7     7 0 25   my ($class, %args) = @_;
45 7         17   my $self = bless $class->_read_config_file, $class;
46              
47 7         25   for my $f (qw( user api_key )) {
48 13 100       37     $self->{$f} = $args{$f} if exists $args{$f};
49 13 100       55     confess("Missing required parameter '$f' to new(), ") unless $self->{$f};
50               }
51              
52 5         12   $self->{base_url} = 'https://api.notifo.com/v1';
53 5         51   $self->{auth_hdr} = encode_base64(join(':', @$self{qw(user api_key)}), '');
54              
55 5         55   return $self;
56             }
57              
58              
59             =method parse_response
60            
61             Accepts two parameters, a HTTP response code and the response content.
62             It parses the content, adds the HTTP response code and returns a hashref
63             with all the fields.
64            
65             The following fields are present on all responses:
66            
67             =over 4
68            
69             =item status
70            
71             A string, either C<success> or C<error>.
72            
73             =item http_code
74            
75             The HTTP code of the response.
76            
77             =item response_code
78            
79             A Notifo.com integer response code.
80            
81             =item response_message
82            
83             A text description of the response. Specially useful with
84             C<status> C<error>.
85            
86             =back
87            
88             =cut
89             sub parse_response {
90 3     3 0 7   my ($self, $http_code, $content) = @_;
91              
92 3         56   my $res = decode_json($content);
93 3         5   $res->{http_code} = $http_code;
94              
95 3         28   return $res;
96             }
97              
98              
99             =method send_notification
100            
101             Prepares a request for the C<send_notification> API.
102            
103             Accepts a hash with options. The following options are supported:
104            
105             =over 4
106            
107             =item msg
108            
109             The notification message. This parameter is B<required>.
110            
111             =item to
112            
113             The destination user. If the API username/key pair used is of a User
114             account, then this parameter is ignored and can be safelly ommited.
115            
116             A User account can only send notifications to itself. A Service account
117             can send notifications to all his subscribed users.
118            
119             =item label
120            
121             A label describing the application that is sending the
122             notification. With Service accounts, this option is ignored and the
123             Service Name is used.
124            
125             =item title
126            
127             The title or subject of the notification.
128            
129             =item uri
130            
131             The URL for the event. On some clients you can click the notification and jump to this URL.
132            
133             =back
134            
135             The return value is a hashref with all the relevant information to
136             perform the HTTP request: the url and the method to use, the
137             Authorization header, and the query form fields.
138            
139             An example:
140            
141             url => "https://api.notifo.com/v1/send_notification",
142             method => "POST",
143             args => {
144             label => "my application",
145             msg => "hello there!",
146             title => "welcome",
147             to => "user_x",
148             uri => "http://www.example.com/welcome/"
149             },
150             headers => {
151             Authorization => "bWU6bXlfa2V5"
152             },
153            
154             The following keys are always present in the hashref:
155            
156             =over 4
157            
158             =item url
159            
160             The URL where the HTTP request should be sent to.
161            
162             =item method
163            
164             The HTTP method to use.
165            
166             =item args
167            
168             A hashref with all the URL query form fields and values.
169            
170             =item headers
171            
172             A hashref with all the headers to include in the HTTP request.
173            
174             =back
175            
176             =cut
177             sub send_notification {
178 7     7 0 27   my ($self, %args) = @_;
179              
180 7         55   my %call = (
181                 url => "$self->{base_url}/send_notification",
182                 method => 'POST',
183                 headers => {Authorization => $self->{auth_hdr}},
184                 args => {},
185               );
186              
187 7         25   for my $f (qw( to msg label title uri )) {
188 35         37     my $v = $args{$f};
189 35 100       52     next unless defined $v;
190              
191 16         28     $call{args}{$f} = $v;
192               }
193              
194 7 100       25   confess("Missing required argument 'msg', ") unless $call{args}{msg};
195              
196 6         62   return \%call;
197             }
198              
199             =method config_file
200            
201             Returns the configuration file that this module will attempt to use.
202            
203             =cut
204             sub config_file {
205 10     10 0 16   my ($self) = @_;
206              
207 10   66     115   return $ENV{NOTIFO_CFG} || catfile(File::HomeDir->my_home, '.notifo.rc');
208             }
209              
210              
211             =head1 CONFIGURATION FILE
212            
213             The configuration file is line based. Empty lines os just spaces/tabs,
214             or lines starting with # are ignored.
215            
216             All other lines are parsed for commands, in the form
217             C<command separator value>. The C<separator> can be a C<=> or a C<:>.
218            
219             See the L<CONSTRUCTORS|new() constructor> for the commands you can use,
220             they are the same ones as the accepted options.
221            
222             =cut
223             sub _read_config_file {
224 7     7   13   my ($self) = @_;
225 7         9   my %opts;
226              
227 7         16   my $fn = $self->config_file;
228 7 100       472   return \%opts unless -r $fn;
229              
230 2 50       99   open(my $fh, '<', $fn) || confess("Could not open file '$fn': $!, ");
231              
232 2         57   while (my $l = <$fh>) {
233 16         18     chomp($l);
234 16         113     $l =~ s/^\s*(#.*)?|\s*$//g;
235 16 100       44     next unless $l;
236              
237 6         29     my ($k, $v) = $l =~ m/(\S+)\s*[=:]\s*(.*)/;
238 6 50       15     confess("Could not parse line $. of $fn ('$l'), ") unless $k;
239              
240 6         31     $opts{$k} = $v;
241               }
242              
243 2         59   return \%opts;
244             }
245              
246             1;
247              
248             __END__
249            
250             =head1 SYNOPSIS
251            
252             ## Reads user and api_key from configuration file
253             my $pn = Protocol::Notifo->new;
254            
255             ## Use a particular user and api_key, overrides configuration file
256             my $pn = Protocol::Notifo->new(user => 'me', api_key => 'my_key');
257            
258             my $req = $pn->send_notification(msg => 'Hi!');
259            
260             .... send $req, get a response back ....
261            
262             my $res = $pn->parse_response($response_http_code, $response_body);
263            
264             .... do stuff with $res ....
265            
266            
267             =head1 DESCRIPTION
268            
269             This module provides a API to prepare requests to the
270             L<http://api.notifo.com/|notifo.com API>.
271            
272             The module doesn't actually execute a HTTP request. It only prepares
273             all the information required for such request to be performed. As such
274             this module is not to be used by end users, but by writters of the
275             Notifo.com API.
276            
277             If you are an end-user and want to call the API, you should look into
278             the modules L<WebService::Notifo> and L<AnyEvent::WebService::Notifo>.
279            
280             This module supports both the User API and the Service API.
281             Differences between the behaviour of the two are noted in this
282             documentation where relevant.
283            
284             You need a Notifo.com account to be able to use this module. The account
285             will give you access to a API username, and a API key. Both are required
286             arguments of our L</CONSTRUCTORS|constructors>.
287            
288             The module also supports a configuration file. See
289             L</config_file|config_file()> to learn which configuration files are
290             loaded automatically, if found.
291            
292             For all the details of this API, check out the site
293             L<http://api.notifo.com/|http://api.notifo.com/>.
294            
295            
296             =head1 TODO
297            
298             Future versions of this module will implement the other APIs:
299            
300             =over 4
301            
302             =item subscribe_user
303            
304             =item send_message
305            
306             =back
307            
308             Patches welcome.
309            
310            
311             =cut
312