File Coverage

lib/Mojolicious/Plugin/Pingen.pm
Criterion Covered Total %
statement 110 118 93.2
branch 28 40 70.0
condition 7 12 58.3
subroutine 23 23 100.0
pod 1 1 100.0
total 169 194 87.1


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Pingen;
2 2     2   2058 use Mojo::Base 'Mojolicious::Plugin';
  2         7  
  2         18  
3 2     2   504 use Mojo::UserAgent;
  2         5  
  2         25  
4 2     2   63 use Mojo::JSON;
  2         4  
  2         107  
5 2     2   10 use POSIX qw(strftime);
  2         4  
  2         21  
6 2     2   137 use Mojo::Exception;
  2         3  
  2         25  
7 2   50 2   89 use constant DEBUG => $ENV{MOJO_PINGEN_DEBUG} || 0;
  2         4  
  2         4773  
8             our $VERSION = '0.2.1';
9              
10             =head1 NAME
11              
12             Mojolicious::Plugin::Pingen - Print Package Send Physical letters
13              
14             =head1 VERSION
15              
16             0.2.0
17              
18             =head1 DESCRIPTION
19              
20             L is a plugin for the L web
21             framework which allows you to do communicate with pingen.com.
22              
23             This module is EXPERIMENTAL. The API can change at any time. Let me know
24             if you are using it.
25              
26             =head1 SYNOPSIS
27              
28             =head2 Production mode
29              
30             use Mojolicious::Lite;
31             plugin Pingen => { apikey => $ENV{SUPER_SECRET_PINGEN_KEY} };
32              
33             post '/send' => sub {
34             my $c = shift;
35             $c->delay(
36             sub { $c->pingen->document->upload($c->param('pdf'), shift->begin) },
37             sub {
38             my ($delay, $res) = @_;
39             return $c->reply->exception($res->{errormessage}) if $res->{error};
40             $c->pingen->document->send($res->{id},{
41             speed => 1
42             },$delay->begin)
43             },
44             sub {
45             my ($delay, $res) = @_;
46             return $c->reply->exception($err) if $err;
47             return $c->render(text => "Delivery of $res->{id} is scheduled!");
48             }
49             );
50             );
51              
52             =head2 Testing mode
53              
54             use Mojolicious::Lite;
55             plugin Pingen => { mocked => 1 };
56              
57             Setting C will enable this plugin to work without an actual connection
58             to pingen.com. This is done by replicating the behavior of Pingen for those API we support. This is
59             especially useful when writing unit tests.
60              
61             The apikey for the mocked interface is: sk_test_super_secret_key
62              
63             The following routes will be added to your application to mimic Pidgen:
64              
65             =over
66              
67             =item * POST /mocked/pingen/document/upload
68              
69             =item * POST /mocked/pingen/document/send
70              
71             =item * POST /mocked/pingen/document/delete
72              
73             =item * POST /mocked/pingen/send/cancel
74              
75             =back
76              
77             =cut
78              
79              
80             =head1 ATTRIBUTES
81              
82             =head2 base_url
83              
84             $str = $self->base_url;
85              
86             This is the location to Stripe payment solution. Will be set to
87             L.
88              
89             =head2 apikey
90              
91             $str = $self->apikey;
92              
93             The value for the private API key. Available in the Stripe admin gui.
94              
95             =head2 exceptions
96              
97             This will cause exceptions to be thrown if there is any problem
98             with submitting the invoice to pingen.
99              
100             =cut
101              
102             has base_url => 'https://api.pingen.com/';
103             has apikey => 'sk_test_super_secret_key';
104             has exceptions => 0;
105             has _ua => sub { Mojo::UserAgent->new; };
106              
107             =head1 HELPERS
108              
109             =head2 pingen.document.upload
110              
111             my $json = $c->pingen->document->upload($asset[,\%args]);
112             $c->pingen->document->upload($mojoUpload[,\%args], sub { my ($c, $json) = @_; });
113              
114             Upload a L object containint a (PDF file). Pingen will analyze the content of the pdf and figure out
115             how and where to mail it.
116              
117             C<$json> is the response object from pingen.
118              
119             C<%args> can contain the following optional parameters:
120              
121             =over
122              
123             =item * color
124              
125             Print color: 0 = B/W, 1 = Color, 2 = Mixed (optimized)
126              
127             =item * duplex
128              
129             Paper hadling: 0 = simplex, 1 = duplex
130              
131             =item * rightaddress
132              
133             Where in the document is the address: 0 = Address left, 1 = Address right.
134              
135             =back
136              
137             =head2 pingen.document.send
138              
139             my $json = $c->pingen->document->send($id,\%args);
140             $c->pingen->document->send($id,\%args, sub { my ($c, $json) = @_; });
141              
142             Use the C<$id> returned from the upload call to actually send the document.
143              
144             C<$json> is a response object from pingen.
145              
146             C<%args> can contain the following parameters:
147              
148             =over
149              
150             =item * speed
151              
152             Delivery Speed. Varies by country. Use L to get a list
153             of speeds for your country. In general 1 = Priority, 2 = Economy.
154              
155             =item * envelope
156              
157             If you have designed your own envelope in the pingen webinterface, hunt take the html inspector to
158             the source code of the admin ui to determine the id of your envelope. You can then refer to it using that id.
159              
160             =back
161              
162             =head2 pingen.document.delete
163              
164             my $json = $c->pingen->document->delete($id);
165             $c->pingen->document->delete($id, sub { my ($c, $json) = @_; });
166              
167             Use the C<$id> returned from upload call to delete the document. Note that this will not
168             cancel any pending send tasks.
169              
170             C<$json> is a response object from pingen.
171              
172             =head2 pingen.send.cancel
173              
174             my $json = $c->pingen->send->cancel($id);
175             $c->pingen->send->cancel($id, sub { my ($c, $json) = @_; });
176              
177             cancel the given send task. Use the ID returned from the send call.
178              
179             C<$err> is a string describing the error. Will be empty string on success.
180             C<$json> is a response object from pingen.
181              
182             =head1 METHODS
183              
184             =head2 register
185              
186             $app->plugin(Pingen => \%config);
187              
188             Called when registering this plugin in the main L application.
189              
190             =cut
191              
192             sub register {
193 2     2 1 85 my ($self, $app, $config) = @_;
194              
195             # copy config to this object
196 2         7 for (grep { $self->can($_) } keys %$config) {
  3         27  
197 1         8 $self->{$_} = $config->{$_};
198             }
199             # self contained
200 2 50       14 $self->_mock_interface($app, $config) if $config->{mocked};
201              
202 2     5   960 $app->helper('pingen.document.upload' => sub { $self->_document_upload(@_); });
  5         76438  
203 2     5   80 $app->helper('pingen.document.send' => sub { $self->_document_send(@_); });
  5         621  
204 2     3   81 $app->helper('pingen.document.delete' => sub { $self->_document_delete(@_); });
  3         275  
205 2     4   50 $app->helper('pingen.send.cancel' => sub { $self->_send_cancel(@_); });
  4         412  
206             }
207              
208             sub _document_upload {
209 5     5   15 my ($self, $c, $upload, $args, $cb) = @_;
210 5         29 my $ua = $self->_ua;
211 5   100     57 $args ||= {};
212 5 100       24 if (ref $args eq 'CODE'){
213 4         7 $cb = $args;
214 4         10 $args = {};
215             }
216 5         23 my %form = ( send => 0 );
217 5 50       17 if (ref $args eq 'HASH'){
218 5         15 for my $key (qw(color duplex rightaddress)){
219 15 50       41 $form{$key} = $args->{$key} if defined $args->{$key};
220             }
221             }
222              
223 5         28 my $URL = $self->base_url.'/document/upload/token/'.$self->apikey;
224 5         84 my $data = {
225             %form,
226             file => { file => $upload->asset, filename => $upload->filename },
227             };
228 5 100       75 if (ref $cb eq 'CODE'){
229 4         71 $ua->post( $URL => form => $data => $self->_build_res_cb($cb));
230             }
231             else {
232 1         6 return $self->_tx_to_json($ua->post( $URL => form => $data));
233             }
234             }
235              
236             sub _document_send {
237 5     5   15 my ($self, $c, $id, $args, $cb) = @_;
238 5         22 my $ua = $self->_ua;
239              
240 5   50     39 $args ||= {};
241 5 50       22 if (ref $args eq 'CODE'){
242 0         0 $cb = $args;
243 0         0 $args = {};
244             }
245 5         9 my %data;
246 5 50       22 if (ref $args eq 'HASH'){
247 5         11 for my $key (qw(speed envelope)){
248 10 100       41 $data{$key} = $args->{$key} if defined $args->{$key};
249             }
250             }
251              
252 5         25 my $URL = $self->base_url.'/document/send/id/'.$id.'/token/'.$self->apikey;
253              
254 5 100       73 if (ref $cb eq 'CODE'){
255 4         17 $ua->post( $URL => json => \%data => $self->_build_res_cb($cb));
256             }
257             else {
258 1         5 return $self->_tx_to_json($ua->post( $URL => json => \%data));
259             }
260             }
261              
262             sub _document_delete {
263 3     3   8 my ($self, $c, $id, $cb) = @_;
264 3         14 my $ua = $self->_ua;
265              
266 3         34 my $URL = $self->base_url.'/document/delete/id/'.$id.'/token/'.$self->apikey;
267              
268 3 100       40 if (ref $cb eq 'CODE'){
269 2         7 $ua->post( $URL => $self->_build_res_cb($cb));
270             }
271             else {
272 1         3 return $self->_tx_to_json($ua->post( $URL ));
273             }
274             }
275              
276             sub _send_cancel {
277 4     4   8 my ($self, $c, $id, $cb) = @_;
278 4         17 my $ua = $self->_ua;
279              
280 4         32 my $URL = $self->base_url.'/send/cancel/id/'.$id.'/token/'.$self->apikey;
281              
282 4 100       58 if (ref $cb eq 'CODE'){
283 3         12 $ua->post( $URL => $self->_build_res_cb($cb));
284             }
285             else {
286 1         4 return $self->_tx_to_json($ua->post( $URL ));
287             }
288             }
289              
290             sub _tx_to_json {
291 17     17   10720 my $self = shift;
292 17         30 my $tx = shift;
293 17   50     45 my $error = $tx->error || {};
294 17         348 my $json;
295 17 50       82 if ($error->{code}){
296             $json = {
297             error => Mojo::JSON::true,
298             errormessage => $error->{message},
299             errorcode => $error->{code}
300 0         0 };
301             Mojo::Exception->throw($json->{errormessage})
302 0 0       0 if $self->exceptions;
303             }
304             else {
305 17   50     54 $json = $tx->res->json || {};
306             }
307 17         5336 return $json;
308             }
309              
310             sub _build_res_cb {
311 13     13   22 my $self = shift;
312 13         30 my $cb = shift;
313             return sub {
314 13     13   41048 my ($c,$tx) = @_;
315 13         58 $c->$cb($self->_tx_to_json($tx));
316             }
317 13         98 }
318              
319             sub _mock_interface {
320 2     2   5 my ($self, $app) = @_;
321 2         4 my $apikey = 'sk_test_super_secret_key';
322              
323 2         8 $self->_ua->server->app($app);
324 2         146 $self->base_url('/mocked/pingen');
325 2         16 push @{$app->renderer->classes}, __PACKAGE__;
  2         19  
326              
327             $app->routes->post( '/mocked/pingen/document/upload/token/:apikey' => sub {
328 5     5   56423 my $c = shift;
329 5 50       51 if ($c->stash('apikey') eq $apikey){
330 5   50     85 $c->render(json => {
331             'error' => Mojo::JSON::false,
332             'item' => {
333             'sent' => 0,
334             'size' => $c->param('file')->size,
335             'pages' => 1,
336             'fileremoved' => 0,
337             'requirement_failure' => 0,
338             'pagetype' => [
339             {
340             'type' => 2,
341             'number' => 1,
342             'color' => 0
343             }
344             ],
345             'filename' => $c->param('file')->filename,
346             'date' => strftime("%Y-%m-%d %H:%M:%S",gmtime(time)),
347             'rightaddress' => $c->param('rightaddress') // 0,
348             'status' => 1,
349             'country' => 'CH',
350             'user_id' => 902706832,
351             'id' => 253193787,
352             'address' => "Tobias Oetiker\nAarweg 15\n4600 Olten\nSchweiz",
353             },
354             'id' => 253193787
355             });
356             }
357             else {
358 0         0 $c->render(json => {
359             error => Mojo::JSON::true,
360             errormessage => 'Your token is invalid or expired',
361             errorcode => 99400
362             });
363             }
364 2         49 });
365             $app->routes->post( '/mocked/pingen/document/send/id/:id/token/:apikey' => sub {
366 5     5   18962 my $c = shift;
367 5 50       21 if ($c->stash('apikey') eq $apikey){
368 5 100       73 if ($c->stash('id') == 253193787){
369 4         61 $c->render(json => {
370             error => Mojo::JSON::false,
371             id => 21830180,
372             });
373             }
374             else {
375 1         16 $c->render(json => {
376             error => Mojo::JSON::true,
377             errorcode => 15016,
378             errormessage => 'You do not have rights to access this object'
379             });
380             }
381             }
382             else {
383 0         0 $c->render(json => {
384             error => Mojo::JSON::true,
385             errormessage => 'Your token is invalid or expired',
386             errorcode => 99400
387             });
388             }
389 2         1240 });
390             $app->routes->post( '/mocked/pingen/document/delete/id/:id/token/:apikey' => sub {
391 3     3   10552 my $c = shift;
392 3 50       13 if ($c->stash('apikey') eq $apikey){
393 3 100       59 if ($c->stash('id') == 253193787){
394 2         31 $c->render(json => {
395             error => Mojo::JSON::false,
396             });
397             }
398             else {
399 1         15 $c->render(json => {
400             error => Mojo::JSON::true,
401             errorcode => 15016,
402             errormessage => 'You do not have rights to access this object'
403             });
404             }
405             }
406             else {
407 0         0 $c->render(json => {
408             error => Mojo::JSON::true,
409             errormessage => 'Your token is invalid or expired',
410             errorcode => 99400
411             });
412             }
413 2         980 });
414             $app->routes->post( '/mocked/pingen/send/cancel/id/:id/token/:apikey' => sub {
415 4     4   13924 my $c = shift;
416 4 50       14 if ($c->stash('apikey') eq $apikey){
417 4 100       55 if ($c->stash('id') == 21830180){
418 3         65 $c->render(json => {
419             error => Mojo::JSON::false,
420             });
421             }
422             else {
423 1         15 $c->render(json => {
424             error => Mojo::JSON::true,
425             errorcode => 15016,
426             errormessage => 'You do not have rights to access this object'
427             });
428             }
429             }
430             else {
431 0           $c->render(json => {
432             error => Mojo::JSON::true,
433             errormessage => 'Your token is invalid or expired',
434             errorcode => 99400
435             });
436             }
437 2         1082 });
438             }
439             1;
440              
441             __END__