File Coverage

lib/WebService/GoShippo.pm
Criterion Covered Total %
statement 27 70 38.5
branch 0 6 0.0
condition n/a
subroutine 9 18 50.0
pod 4 4 100.0
total 40 98 40.8


line stmt bran cond sub pod time code
1 1     1   603 use strict;
  1         2  
  1         27  
2 1     1   5 use warnings;
  1         1  
  1         41  
3             package WebService::GoShippo;
4             $WebService::GoShippo::VERSION = '0.0002';
5 1     1   423 use HTTP::Thin;
  1         78156  
  1         35  
6 1     1   460 use HTTP::Request::Common qw/GET DELETE PUT POST/;
  1         3729  
  1         68  
7 1     1   446 use HTTP::CookieJar;
  1         29982  
  1         44  
8 1     1   658 use JSON;
  1         7655  
  1         5  
9 1     1   135 use URI;
  1         3  
  1         23  
10 1     1   464 use Ouch;
  1         1455  
  1         4  
11 1     1   594 use Moo;
  1         10752  
  1         4  
12              
13             =head1 NAME
14              
15             WebService::GoShippo - A simple client to L.
16              
17             =head1 VERSION
18              
19             version 0.0002
20              
21             =head1 SYNOPSIS
22              
23             use WebService::GoShippo;
24              
25             my $shippo = WebService::GoShippo->new(token => 'XXXXXXXXXXxxxxxxxxxxxx', version => '2018-08-28');
26              
27             my $addresses = $shippo->get('addresses');
28              
29             =head1 DESCRIPTION
30              
31             A light-weight wrapper for Shippo's RESTful API (an example of which can be found at: L). This wrapper basically hides the request cycle from you so that you can get down to the business of using the API. It doesn't attempt to manage the data structures or objects the web service interfaces with.
32              
33             The module takes care of all of these things for you:
34              
35             =over 4
36              
37             =item Adding authentication headers
38              
39             C adds an authentication header of the type "Authorization: C<$tj-Etoken>" to each request.
40              
41             =item Adding api version number to request header.
42              
43             C can optionally add a header selecting a particular version of the API C< $tj-Eversion > to each request you submit. If you do not request a particular API version,
44             then Shippo will use the version specified in your account settings.
45              
46             =item PUT/POST data translated to JSON
47              
48             When making a request like:
49              
50             $tj->post('customers', { customer_id => '27', exemption_type => 'non_exempt', name => 'Andy Dufresne', });
51              
52             The data in POST request will be translated to JSON using and encoded to UTF8.
53              
54             =item Response data is deserialized from JSON and returned from each call.
55              
56             =back
57              
58             =head1 EXCEPTIONS
59              
60             All exceptions in C are handled by C. A 500 exception C<"Server returned unparsable content."> is returned if Shippo's server returns something that isn't JSON. If the request isn't successful, then an exception with the code and response and string will be thrown.
61              
62             =head1 METHODS
63              
64             The following methods are available.
65              
66             =head2 new ( params )
67              
68             Constructor.
69              
70             =over
71              
72             =item params
73              
74             A hash of parameters.
75              
76             =over
77              
78             =item token
79              
80             Your token for accessing Shippo's API. Required.
81              
82             =item version
83              
84             The version of the API that you are using, like '2018-02-08', '2017-08-01', etc. Optional. If this is left off, then the version setup in your account will be used.
85              
86             =cut
87              
88             =item debug_flag
89              
90             Just a spare, writable flag so that users of the object should log debug information, since GoShippo will likely ask for request/response pairs when
91             you're having problems. Hint hint.
92              
93             my $sales_tax = $taxjar->get('taxes', $order_information);
94             if ($taxjar->debug_flag) {
95             $log->info($taxjar->last_response->request->as_string);
96             $log->info($taxjar->last_response->decoded_content);
97             }
98              
99             =cut
100              
101             has token => (
102             is => 'ro',
103             required => 1,
104             );
105              
106             has version => (
107             is => 'ro',
108             required => 0,
109             );
110              
111             has debug_flag => (
112             is => 'rw',
113             required => 0,
114             default => sub { 0 },
115             );
116              
117             =item agent
118              
119             A LWP::UserAgent compliant object used to keep a persistent cookie_jar across requests. By default this module uses HTTP::Thin, but you can supply another object when
120             creating a WebService::GoShippo object.
121              
122             =back
123              
124             =back
125              
126             =cut
127              
128             has agent => (
129             is => 'ro',
130             required => 0,
131             lazy => 1,
132             builder => '_build_agent',
133             );
134              
135             sub _build_agent {
136 0     0     return HTTP::Thin->new( cookie_jar => HTTP::CookieJar->new() )
137             }
138              
139             =head2 last_response
140              
141             The HTTP::Response object from the last request/reponse pair that was sent, for debugging purposes.
142              
143             =cut
144              
145             has last_response => (
146             is => 'rw',
147             required => 0,
148             );
149              
150             =head2 get(path, params)
151              
152             Performs a C request, which is used for reading data from the service.
153              
154             =over
155              
156             =item path
157              
158             The path to the REST interface you wish to call.
159              
160             =item params
161              
162             A hash reference of parameters you wish to pass to the web service. These parameters will be added as query parameters to the URL for you.
163              
164             =back
165              
166             =cut
167              
168             sub get {
169 0     0 1   my ($self, $path, $params) = @_;
170 0           my $uri = $self->_create_uri($path);
171 0           $uri->query_form($params);
172 0           return $self->_process_request( GET $uri->as_string );
173             }
174              
175             =head2 delete(path)
176              
177             Performs a C request, deleting data from the service.
178              
179             =over
180              
181             =item path
182              
183             The path to the REST interface you wish to call.
184              
185             =item params
186              
187             A hash reference of parameters you wish to pass to the web service. These parameters will be added as query parameters to the URL for you.
188              
189             =back
190              
191             =cut
192              
193             sub delete {
194 0     0 1   my ($self, $path, $params) = @_;
195 0           my $uri = $self->_create_uri($path);
196 0           $uri->query_form($params);
197 0           return $self->_process_request( DELETE $uri->as_string );
198             }
199              
200             =head2 put(path, json)
201              
202             Performs a C request, which is used for updating data in the service.
203              
204             =over
205              
206             =item path
207              
208             The path to the REST interface you wish to call.
209              
210             =item params
211              
212             A hash reference of parameters you wish to pass to Shippo. This will be translated to JSON.
213              
214             =back
215              
216             =cut
217              
218             sub put {
219 0     0 1   my ($self, $path, $params) = @_;
220 0           my $uri = $self->_create_uri($path);
221 0           my %headers = ( Content => to_json($params, { utf8 => 1, }), );
222 0           return $self->_process_request( POST $uri->as_string, %headers );
223             }
224              
225             =head2 post(path, params, options)
226              
227             Performs a C request, which is used for creating data in the service.
228              
229             =over
230              
231             =item path
232              
233             The path to the REST interface you wish to call.
234              
235             =item params
236              
237             A hash reference of parameters you wish to pass to Shippo. They will be encoded as JSON.
238              
239             =back
240              
241             =head2 Notes
242              
243             The path you provide as arguments to the request methods C should not have a leading slash.
244              
245             As of early 2019:
246              
247             The current version of their API is '2018-02-08'. There is no default value for the C parameter, so please provide this when creating a WebService::GoShippo object.
248              
249             Shippo provides a free testing mode for prototyping your code but it is not feature complete. The test mode is accessed by passing a separate token from your regular production token. Please visit their website for details as to what works in test mode, and what works.
250              
251             =cut
252              
253             sub post {
254 0     0 1   my ($self, $path, $params) = @_;
255 0           my $uri = $self->_create_uri($path);
256 0           my %headers = ( Content => to_json($params, { utf8 => 1, }), );
257 0           return $self->_process_request( POST $uri->as_string, %headers );
258             }
259              
260             sub _create_uri {
261 0     0     my $self = shift;
262 0           my $path = shift;
263 0           return URI->new(join '/', 'https://api.goshippo.com', $path);
264             }
265              
266             sub _add_headers {
267 0     0     my $self = shift;
268 0           my $request = shift;
269 0           $request->header( Authorization => 'ShippoToken '.$self->token() );
270 0           $request->header( 'Content-Type' => 'application/json' );
271 0           $request->header( 'Accept-Charset' => 'utf-8' );
272 0 0         if ($self->version) {
273 0           $request->header( 'Shippo-API-Version' => $self->version );
274             }
275 0           return;
276             }
277              
278             sub _process_request {
279 0     0     my $self = shift;
280 0           my $request = shift;
281 0           $self->_add_headers($request);
282 0           my $response = $self->agent->request($request);
283 0           $response->request($request);
284 0           $self->last_response($response);
285 0           $self->_process_response($response);
286             }
287              
288             sub _process_response {
289 0     0     my $self = shift;
290 0           my $response = shift;
291 0           my $result = eval { from_json($response->decoded_content, {utf8 => 1, }) };
  0            
292 0 0         if ($@) {
    0          
293 0           ouch 500, 'Server returned unparsable content.', { error => $@, content => $response->decoded_content };
294             }
295             elsif ($response->is_success) {
296 0           return $result;
297             }
298             else {
299 0           ouch $response->code, $response->as_string;
300             }
301             }
302              
303             =head1 PREREQS
304              
305             L
306             L
307             L
308             L
309             L
310             L
311             L
312              
313             =head1 SUPPORT
314              
315             =over
316              
317             =item Repository
318              
319             L
320              
321             =item Bug Reports
322              
323             L
324              
325             =back
326              
327             =head1 AUTHOR
328              
329             Colin Kuskie
330              
331             =head1 LEGAL
332              
333             This module is Copyright 2019 Plain Black Corporation. It is distributed under the same terms as Perl itself.
334              
335             =cut
336              
337             1;