File Coverage

blib/lib/WWW/JSONAPI.pm
Criterion Covered Total %
statement 35 61 57.3
branch 2 12 16.6
condition 2 11 18.1
subroutine 9 15 60.0
pod 7 7 100.0
total 55 106 51.8


line stmt bran cond sub pod time code
1             package WWW::JSONAPI;
2            
3 2     2   43105 use 5.006;
  2         9  
  2         113  
4 2     2   13 use strict;
  2         4  
  2         81  
5 2     2   11 use warnings FATAL => 'all';
  2         7  
  2         325  
6 2     2   2589 use LWP;
  2         151258  
  2         77  
7 2     2   26 use HTTP::Request;
  2         3  
  2         49  
8 2     2   4181 use JSON;
  2         52582  
  2         12  
9 2     2   329 use Carp;
  2         4  
  2         1710  
10            
11             =head1 NAME
12            
13             WWW::JSONAPI - Very thin and inadequate wrapper for JSON APIs
14            
15             =head1 VERSION
16            
17             Version 0.01
18            
19             =cut
20            
21             our $VERSION = '0.01';
22            
23            
24             =head1 SYNOPSIS
25            
26             This module contains utterly minimal functionality for interacting with JSON-based REST services with or without SSL.
27             It resulted from my development of L, and has the purpose of providing a very thin but convenient
28             abstraction layer on top of LWP and JSON for that API. Other than those, it has no dependencies.
29             Version 0.01 contains only those methods needed by WWW::KeePassRest, so really it shouldn't even be considered
30             Version 0.01, but rather 0.00001 or so.
31            
32             For a more feature-rich JSON module, you'll probably want L. Seriously. Anything but this one.
33            
34             use WWW::JSONAPI;
35            
36             my $json = WWW::JSONAPI->new(cert_file => 'cert/wwwkprcert.pem',
37             key_file => 'cert/wwwkprkey.pem',
38             base_url => 'https://localhost:12984/keepass/');
39            
40             my $hashref = $json->GET_json ("entry/$uuid");
41            
42             The last request and response are always available if you have something else you want to do with them.
43            
44             If the server responds with anything but a 200, the module croaks with the status line of the response.
45             Note that this also applies to 301/302 forwards and the like. Failure to connect is flagged with a C<500 Can't connect ___>
46             error; this comes from LWP and is still grounds for croaking.
47            
48             =head1 SUBROUTINES/METHODS
49            
50             Aside from C and C, the module only includes the
51             calls needed to support WWW::KeePassRest, with no attempt at completeness.
52            
53             The form of each method is [input]_[method]_[output], where [input] is either "json" or omitted, [method] is GET, POST,
54             PUT, or DELETE, and [output] is either "json" for output that should be JSON-decoded or "string" for output that should
55             be returned with no decoding.
56            
57             =head2 new
58            
59             Sets up the LWP user agent, including SSL parameters. The following options can be provided:
60            
61             =over
62            
63             =item C: the base URL for the API; this will be prepended to every request URL.
64            
65             =item C: the relative or absolute path to a certificate file for SSL
66            
67             =item C: the corresponding key file
68            
69             =back
70            
71             If the base_url option is omitted, you will have to specify the full URL for every request.
72             This would be weird, but who am I to say it's not a perfectly valid way of doing things?
73            
74             =cut
75            
76             sub new {
77 1     1 1 731 my $self = bless {}, shift;
78 1         4 my %opts = @_;
79            
80 1         12 $self->{ua} = LWP::UserAgent->new();
81 1         4917 $self->{q} = undef; # Last query.
82 1         3 $self->{r} = undef; # Last response.
83            
84 1 50 33     14 if (defined $opts{cert_file} || defined $opts{key_file}) {
85 0   0     0 my $cert_file = $opts{cert_file} || croak ('Need both cert and key files for WWW::JSONAPI, or neither');
86 0   0     0 my $key_file = $opts{key_file} || croak ('Need both cert and key files for WWW::JSONAPI, or neither');
87 0         0 $self->{ua}->ssl_opts (
88             SSL_version => 'SSLv3',
89             verify_hostname => 0,
90             SSL_cert_file => $cert_file,
91             SSL_key_file => $key_file,
92             );
93             }
94            
95 1   50     11 $self->{base_url} = $opts{base_url} || '';
96 1         25 $self->{j} = JSON->new->utf8;
97            
98 1         6 return $self;
99             }
100            
101             =head2 ua, req, res
102            
103             =over
104            
105             =item C: returns the LWP user agent for direct access
106            
107             =item C: returns the last request object
108            
109             =item C: returns the last response object
110            
111             =back
112            
113             =cut
114            
115 0     0 1 0 sub ua { $_[0]->{ua} }
116            
117            
118            
119             # ------------------------------------
120             # Error handling
121             # ------------------------------------
122            
123             sub _bad_retcode {
124 0     0   0 my $self = shift;
125 0         0 croak $self->{r}->status_line;
126             }
127            
128             =head2 json_POST_json
129            
130             Does a POST request, taking a hashref of parameters to the POST and expecting JSON back, which it converts to
131             a hashref for return to the caller.
132            
133             =cut
134            
135             sub json_POST_json {
136 0     0 1 0 my ($self, $url, $out) = @_;
137 0         0 $self->{q} = HTTP::Request->new (POST => $self->{base_url} . $url, ['Content-Type' => 'application/json'], $self->{j}->encode($out));
138 0         0 $self->{r} = $self->{ua}->request($self->{q});
139 0 0       0 return $self->_bad_retcode unless $self->{r}->is_success;
140 0         0 return $self->{j}->decode($self->{r}->content);
141             }
142            
143             =head2 json_POST_string
144            
145             Same as json_POST_json, but simply returns the literal return value without attempting to decode it.
146            
147             =cut
148            
149             sub json_POST_string {
150 0     0 1 0 my ($self, $url, $out) = @_;
151 0         0 $self->{q} = HTTP::Request->new (POST => $self->{base_url} . $url, ['Content-Type' => 'application/json'], $self->{j}->encode($out));
152 0         0 $self->{r} = $self->{ua}->request($self->{q});
153 0 0       0 return $self->_bad_retcode unless $self->{r}->is_success;
154 0         0 return $self->{r}->content;
155             }
156            
157             =head2 json_PUT_string
158            
159             Does a PUT request, taking a hashref of parameters to the PUT and expecting a string back that will not be JSON-decoded.
160            
161             =cut
162            
163             sub json_PUT_string {
164 0     0 1 0 my ($self, $url, $out) = @_;
165 0         0 $self->{q} = HTTP::Request->new (PUT => $self->{base_url} . $url, ['Content-Type' => 'application/json'], $self->{j}->encode($out));
166 0         0 $self->{r} = $self->{ua}->request($self->{q});
167 0 0       0 return $self->_bad_retcode unless $self->{r}->is_success;
168 0         0 return $self->{r}->content;
169             }
170            
171             =head2 GET_json
172            
173             Performs a GET request on the URL provided, interpreting the return as JSON and returning a hashref.
174            
175             =cut
176            
177             sub GET_json {
178 1     1 1 9 my ($self, $url) = @_;
179 1         11 $self->{q} = HTTP::Request->new (GET => $self->{base_url} . $url);
180 1         15139 $self->{r} = $self->{ua}->request($self->{q});
181 1 50       319562 return $self->_bad_retcode unless $self->{r}->is_success;
182 1         27 return $self->{j}->decode($self->{r}->content);
183             }
184            
185             =head2 DELETE_string
186            
187             Performs a DELETE request on the URL provided, interpreting the return as a string that will not be JSON-decoded.
188            
189             =cut
190            
191             sub DELETE_string {
192 0     0 1   my ($self, $url) = @_;
193 0           $self->{q} = HTTP::Request->new (DELETE => $self->{base_url} . $url);
194 0           $self->{r} = $self->{ua}->request($self->{q});
195 0 0         return $self->_bad_retcode unless $self->{r}->is_success;
196 0           return $self->{r}->content;
197             }
198            
199            
200             =head1 AUTHOR
201            
202             Michael Roberts, C<< >>
203            
204             =head1 BUGS
205            
206             Please report any bugs or feature requests to C, or through
207             the web interface at L. I will be notified, and then you'll
208             automatically be notified of progress on your bug as I make changes.
209            
210            
211            
212            
213             =head1 SUPPORT
214            
215             You can find documentation for this module with the perldoc command.
216            
217             perldoc WWW::JSONAPI
218            
219            
220             You can also look for information at:
221            
222             =over 4
223            
224             =item * RT: CPAN's request tracker (report bugs here)
225            
226             L
227            
228             =item * AnnoCPAN: Annotated CPAN documentation
229            
230             L
231            
232             =item * CPAN Ratings
233            
234             L
235            
236             =item * Search CPAN
237            
238             L
239            
240             =back
241            
242            
243             =head1 ACKNOWLEDGEMENTS
244            
245            
246             =head1 LICENSE AND COPYRIGHT
247            
248             Copyright 2014 Michael Roberts.
249            
250             This program is free software; you can redistribute it and/or modify it
251             under the terms of the the Artistic License (2.0). You may obtain a
252             copy of the full license at:
253            
254             L
255            
256             Any use, modification, and distribution of the Standard or Modified
257             Versions is governed by this Artistic License. By using, modifying or
258             distributing the Package, you accept this license. Do not use, modify,
259             or distribute the Package, if you do not accept this license.
260            
261             If your Modified Version has been derived from a Modified Version made
262             by someone other than you, you are nevertheless required to ensure that
263             your Modified Version complies with the requirements of this license.
264            
265             This license does not grant you the right to use any trademark, service
266             mark, tradename, or logo of the Copyright Holder.
267            
268             This license includes the non-exclusive, worldwide, free-of-charge
269             patent license to make, have made, use, offer to sell, sell, import and
270             otherwise transfer the Package with respect to any patent claims
271             licensable by the Copyright Holder that are necessarily infringed by the
272             Package. If you institute patent litigation (including a cross-claim or
273             counterclaim) against any party alleging that the Package constitutes
274             direct or contributory patent infringement, then this Artistic License
275             to you shall terminate on the date that such litigation is filed.
276            
277             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
278             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
279             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
280             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
281             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
282             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
283             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
284             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
285            
286            
287             =cut
288            
289             1; # End of WWW::JSONAPI