File Coverage

lib/Mojolicious/Plugin/Pingen.pm
Criterion Covered Total %
statement 113 119 94.9
branch 31 40 77.5
condition 9 15 60.0
subroutine 23 23 100.0
pod 1 1 100.0
total 177 198 89.3


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Pingen;
2 3     3   2448 use Mojo::Base 'Mojolicious::Plugin';
  3         5  
  3         24  
3 3     3   652 use Mojo::UserAgent;
  3         5  
  3         32  
4 3     3   85 use Mojo::JSON;
  3         6  
  3         143  
5 3     3   16 use POSIX qw(strftime);
  3         4  
  3         27  
6 3     3   192 use Mojo::Exception;
  3         6  
  3         27  
7 3   50 3   199 use constant DEBUG => $ENV{MOJO_PINGEN_DEBUG} || 0;
  3         14  
  3         7296  
8             our $VERSION = '0.2.2';
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 3     3 1 114 my ($self, $app, $config) = @_;
194              
195             # copy config to this object
196 3         12 for (grep { $self->can($_) } keys %$config) {
  6         47  
197 3         19 $self->{$_} = $config->{$_};
198             }
199             # self contained
200 3 50       20 $self->_mock_interface($app, $config) if $config->{mocked};
201              
202 3     6   1468 $app->helper('pingen.document.upload' => sub { $self->_document_upload(@_); });
  6         89769  
203 3     5   109 $app->helper('pingen.document.send' => sub { $self->_document_send(@_); });
  5         535  
204 3     3   105 $app->helper('pingen.document.delete' => sub { $self->_document_delete(@_); });
  3         281  
205 3     4   80 $app->helper('pingen.send.cancel' => sub { $self->_send_cancel(@_); });
  4         418  
206             }
207              
208             sub _document_upload {
209 6     6   21 my ($self, $c, $upload, $args, $cb) = @_;
210 6         30 my $ua = $self->_ua;
211 6   100     69 $args ||= {};
212 6 100       27 if (ref $args eq 'CODE'){
213 4         4 $cb = $args;
214 4         9 $args = {};
215             }
216 6         29 my %form = ( send => 0 );
217 6 50       25 if (ref $args eq 'HASH'){
218 6         14 for my $key (qw(color duplex rightaddress)){
219 18 50       80 $form{$key} = $args->{$key} if defined $args->{$key};
220             }
221             }
222              
223 6         33 my $URL = $self->base_url.'/document/upload/token/'.$self->apikey;
224 6         99 my $data = {
225             %form,
226             file => { file => $upload->asset, filename => $upload->filename },
227             };
228 6 100       94 if (ref $cb eq 'CODE'){
229 4         19 $ua->post( $URL => form => $data => $self->_build_res_cb($cb));
230             }
231             else {
232 2         14 return $self->_tx_to_json($ua->post( $URL => form => $data));
233             }
234             }
235              
236             sub _document_send {
237 5     5   12 my ($self, $c, $id, $args, $cb) = @_;
238 5         21 my $ua = $self->_ua;
239              
240 5   50     39 $args ||= {};
241 5 50       19 if (ref $args eq 'CODE'){
242 0         0 $cb = $args;
243 0         0 $args = {};
244             }
245 5         7 my %data;
246 5 50       17 if (ref $args eq 'HASH'){
247 5         31 for my $key (qw(speed envelope)){
248 10 100       43 $data{$key} = $args->{$key} if defined $args->{$key};
249             }
250             }
251              
252 5         20 my $URL = $self->base_url.'/document/send/id/'.$id.'/token/'.$self->apikey;
253              
254 5 100       80 if (ref $cb eq 'CODE'){
255 4         15 $ua->post( $URL => json => \%data => $self->_build_res_cb($cb));
256             }
257             else {
258 1         4 return $self->_tx_to_json($ua->post( $URL => json => \%data));
259             }
260             }
261              
262             sub _document_delete {
263 3     3   9 my ($self, $c, $id, $cb) = @_;
264 3         21 my $ua = $self->_ua;
265              
266 3         22 my $URL = $self->base_url.'/document/delete/id/'.$id.'/token/'.$self->apikey;
267              
268 3 100       41 if (ref $cb eq 'CODE'){
269 2         6 $ua->post( $URL => $self->_build_res_cb($cb));
270             }
271             else {
272 1         4 return $self->_tx_to_json($ua->post( $URL ));
273             }
274             }
275              
276             sub _send_cancel {
277 4     4   11 my ($self, $c, $id, $cb) = @_;
278 4         16 my $ua = $self->_ua;
279              
280 4         33 my $URL = $self->base_url.'/send/cancel/id/'.$id.'/token/'.$self->apikey;
281              
282 4 100       55 if (ref $cb eq 'CODE'){
283 3         11 $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 18     18   15151 my $self = shift;
292 18         30 my $tx = shift;
293 18   50     60 my $error = $tx->error || {};
294 18         396 my $json;
295 18 50       79 if ($error->{code}){
296             $json = {
297             error => Mojo::JSON::true,
298             errormessage => $error->{message},
299             errorcode => $error->{code}
300 0         0 };
301             }
302             else {
303 18   50     52 $json = $tx->res->json || {};
304             }
305 18 100 66     5773 if ($self->exceptions and $json->{error}){
306             Mojo::Exception->throw($json->{errormessage})
307 1         34 }
308 17         203 return $json;
309             }
310              
311             sub _build_res_cb {
312 13     13   23 my $self = shift;
313 13         17 my $cb = shift;
314             return sub {
315 13     13   36398 my ($c,$tx) = @_;
316 13         48 $c->$cb($self->_tx_to_json($tx));
317             }
318 13         95 }
319              
320             sub _mock_interface {
321 3     3   7 my ($self, $app) = @_;
322 3         7 my $apikey = 'sk_test_super_secret_key';
323              
324 3         12 $self->_ua->server->app($app);
325 3         203 $self->base_url('/mocked/pingen');
326 3         82 push @{$app->renderer->classes}, __PACKAGE__;
  3         21  
327              
328             $app->routes->post( '/mocked/pingen/document/upload/token/:apikey' => sub {
329 6     6   73937 my $c = shift;
330 6 100       77 if ($c->stash('apikey') eq $apikey){
331 5   50     84 $c->render(json => {
332             'error' => Mojo::JSON::false,
333             'item' => {
334             'sent' => 0,
335             'size' => $c->param('file')->size,
336             'pages' => 1,
337             'fileremoved' => 0,
338             'requirement_failure' => 0,
339             'pagetype' => [
340             {
341             'type' => 2,
342             'number' => 1,
343             'color' => 0
344             }
345             ],
346             'filename' => $c->param('file')->filename,
347             'date' => strftime("%Y-%m-%d %H:%M:%S",gmtime(time)),
348             'rightaddress' => $c->param('rightaddress') // 0,
349             'status' => 1,
350             'country' => 'CH',
351             'user_id' => 902706832,
352             'id' => 253193787,
353             'address' => "Tobias Oetiker\nAarweg 15\n4600 Olten\nSchweiz",
354             },
355             'id' => 253193787
356             });
357             }
358             else {
359 1         26 $c->render(json => {
360             error => Mojo::JSON::true,
361             errormessage => 'Your token is invalid or expired',
362             errorcode => 99400
363             });
364             }
365 3         66 });
366             $app->routes->post( '/mocked/pingen/document/send/id/:id/token/:apikey' => sub {
367 5     5   18649 my $c = shift;
368 5 50       22 if ($c->stash('apikey') eq $apikey){
369 5 100       71 if ($c->stash('id') == 253193787){
370 4         57 $c->render(json => {
371             error => Mojo::JSON::false,
372             id => 21830180,
373             });
374             }
375             else {
376 1         15 $c->render(json => {
377             error => Mojo::JSON::true,
378             errorcode => 15016,
379             errormessage => 'You do not have rights to access this object'
380             });
381             }
382             }
383             else {
384 0         0 $c->render(json => {
385             error => Mojo::JSON::true,
386             errormessage => 'Your token is invalid or expired',
387             errorcode => 99400
388             });
389             }
390 3         1875 });
391             $app->routes->post( '/mocked/pingen/document/delete/id/:id/token/:apikey' => sub {
392 3     3   10813 my $c = shift;
393 3 50       26 if ($c->stash('apikey') eq $apikey){
394 3 100       53 if ($c->stash('id') == 253193787){
395 2         35 $c->render(json => {
396             error => Mojo::JSON::false,
397             });
398             }
399             else {
400 1         15 $c->render(json => {
401             error => Mojo::JSON::true,
402             errorcode => 15016,
403             errormessage => 'You do not have rights to access this object'
404             });
405             }
406             }
407             else {
408 0         0 $c->render(json => {
409             error => Mojo::JSON::true,
410             errormessage => 'Your token is invalid or expired',
411             errorcode => 99400
412             });
413             }
414 3         1522 });
415             $app->routes->post( '/mocked/pingen/send/cancel/id/:id/token/:apikey' => sub {
416 4     4   14366 my $c = shift;
417 4 50       17 if ($c->stash('apikey') eq $apikey){
418 4 100       61 if ($c->stash('id') == 21830180){
419 3         43 $c->render(json => {
420             error => Mojo::JSON::false,
421             });
422             }
423             else {
424 1         16 $c->render(json => {
425             error => Mojo::JSON::true,
426             errorcode => 15016,
427             errormessage => 'You do not have rights to access this object'
428             });
429             }
430             }
431             else {
432 0           $c->render(json => {
433             error => Mojo::JSON::true,
434             errormessage => 'Your token is invalid or expired',
435             errorcode => 99400
436             });
437             }
438 3         1620 });
439             }
440             1;
441              
442             __END__