File Coverage

blib/lib/Net/Todoist.pm
Criterion Covered Total %
statement 31 264 11.7
branch 5 228 2.1
condition 1 14 7.1
subroutine 8 31 25.8
pod 23 25 92.0
total 68 562 12.1


line stmt bran cond sub pod time code
1             package Net::Todoist;
2             $Net::Todoist::VERSION = '0.06';
3              
4             # ABSTRACT: interface to the API for Todoist (a to-do list service)
5              
6 1     1   57462 use strict;
  1         3  
  1         50  
7 1     1   6 use warnings;
  1         2  
  1         143  
8 1     1   1427 use LWP::UserAgent;
  1         65684  
  1         36  
9 1     1   1324 use JSON::XS;
  1         7101  
  1         82  
10 1     1   10 use Carp 'croak';
  1         1  
  1         48  
11 1     1   5 use vars qw/$errstr/;
  1         3  
  1         4099  
12              
13             sub new {
14 1     1 0 14 my $class = shift;
15 1 50       7 my $args = scalar @_ % 2 ? shift : {@_};
16              
17 1 50       6 unless ( $args->{ua} ) {
18 1   50     10 my $ua_args = delete $args->{ua_args} || {};
19 1         14 $args->{ua} = LWP::UserAgent->new(%$ua_args);
20             }
21 1 50       3377 unless ( $args->{json} ) {
22 1         22 $args->{json} = JSON::XS->new->utf8->allow_nonref;
23             }
24              
25 1         6 bless $args, $class;
26             }
27              
28 0     0 0 0 sub errstr { $errstr }
29              
30             sub login {
31 0     0 1 0 my ( $self, $email, $pass ) = @_;
32              
33 0         0 my $resp = $self->{ua}->post(
34             'https://todoist.com/API/login',
35             [
36             email => $email,
37             password => $pass
38             ]
39             );
40 0 0       0 unless ( $resp->is_success ) {
41 0         0 $errstr = $resp->status_line;
42 0         0 return;
43             }
44 0 0       0 if ( $resp->content =~ 'LOGIN_ERROR' ) {
45 0         0 $errstr = $resp->content;
46 0         0 return;
47             }
48              
49 0         0 my $data = $self->{json}->decode( $resp->content );
50 0         0 $self->{token} = $data->{api_token};
51 0         0 return $data;
52             }
53              
54             sub getTimezones {
55 1     1 1 7 my ($self) = @_;
56              
57 1         13 my $resp = $self->{ua}->get('http://todoist.com/API/getTimezones');
58 1 50       473874 unless ( $resp->is_success ) {
59 0         0 $errstr = $resp->status_line;
60 0         0 return;
61             }
62              
63 1         23 my $data = $self->{json}->decode( $resp->content );
64 1 50       292 return wantarray ? @$data : $data;
65             }
66              
67             sub register {
68 0     0 1   my $self = shift;
69 0 0         my $args = scalar @_ % 2 ? shift : {@_};
70              
71 0   0       my $resp = $self->{ua}->post(
72             'https://todoist.com/API/register',
73             [
74             email => $args->{email},
75             full_name => $args->{full_name},
76             password => $args->{password} || $args->{pass},
77             timezone => $args->{timezone}
78             ]
79             );
80 0 0         unless ( $resp->is_success ) {
81 0           $errstr = $resp->status_line;
82 0           return;
83             }
84 0 0         unless ( $resp->content =~ 'api_token' ) {
85 0           $errstr = $resp->content;
86 0           return;
87             }
88              
89 0           my $data = $self->{json}->decode( $resp->content );
90 0           $self->{token} = $data->{api_token};
91 0           return $data;
92             }
93              
94             sub updateUser {
95 0     0 1   my $self = shift;
96              
97             # validate
98 0 0         defined $self->{token}
99             or croak
100             'token must be passed to ->new, or call ->login, ->register before this.';
101              
102 0 0         my $args = scalar @_ % 2 ? shift : {@_};
103              
104 0   0       my $resp = $self->{ua}->post(
105             'https://todoist.com/API/updateUser',
106             [
107             token => $self->{token},
108             email => $args->{email},
109             full_name => $args->{full_name},
110             password => $args->{password} || $args->{pass},
111             timezone => $args->{timezone}
112             ]
113             );
114 0 0         unless ( $resp->is_success ) {
115 0           $errstr = $resp->status_line;
116 0           return;
117             }
118 0 0         unless ( $resp->content =~ 'api_token' ) {
119 0           $errstr = $resp->content;
120 0           return;
121             }
122              
123 0           my $data = $self->{json}->decode( $resp->content );
124 0           return $data;
125             }
126              
127             sub getProjects {
128 0     0 1   my $self = shift;
129              
130             # validate
131 0 0         defined $self->{token}
132             or croak
133             'token must be passed to ->new, or call ->login, ->register before this.';
134              
135 0           my $resp = $self->{ua}
136             ->get("http://todoist.com/API/getProjects?token=$self->{token}");
137 0 0         unless ( $resp->is_success ) {
138 0           $errstr = $resp->status_line;
139 0           return;
140             }
141              
142 0           my $data = $self->{json}->decode( $resp->content );
143 0 0         return wantarray ? @$data : $data;
144             }
145              
146             sub getProject {
147 0     0 1   my ( $self, $project_id ) = @_;
148              
149             # validate
150 0 0         defined $self->{token}
151             or croak
152             'token must be passed to ->new, or call ->login, ->register before this.';
153              
154 0           my $resp =
155             $self->{ua}->get(
156             "http://todoist.com/API/getProject?token=$self->{token}&project_id=$project_id"
157             );
158 0 0         unless ( $resp->is_success ) {
159 0           $errstr = $resp->status_line;
160 0           return;
161             }
162              
163 0           my $data = $self->{json}->decode( $resp->content );
164 0           return $data;
165             }
166              
167             sub addProject {
168 0     0 1   my $self = shift;
169 0 0         my $args = scalar @_ % 2 ? shift : {@_};
170              
171             # validate
172 0 0         defined $self->{token}
173             or croak
174             'token must be passed to ->new, or call ->login, ->register before this.';
175 0 0         defined $args->{name} or croak 'name is required.';
176              
177 0 0         my $resp = $self->{ua}->post(
    0          
    0          
178             'https://todoist.com/API/addProject',
179             [
180             token => $self->{token},
181             name => $args->{name},
182             $args->{color} ? ( color => $args->{color} ) : (),
183             $args->{indent} ? ( indent => $args->{indent} ) : (),
184             $args->{order} ? ( order => $args->{order} ) : (),
185             ]
186             );
187 0 0         unless ( $resp->is_success ) {
188 0           $errstr = $resp->status_line;
189 0           return;
190             }
191 0 0         if ( $resp->content =~ 'ERROR_NAME_IS_EMPTY' ) {
192 0           $errstr = $resp->content;
193 0           return;
194             }
195              
196 0           my $data = $self->{json}->decode( $resp->content );
197 0           return $data;
198             }
199              
200             sub updateProject {
201 0     0 1   my $self = shift;
202 0 0         my $args = scalar @_ % 2 ? shift : {@_};
203              
204             # validate
205 0 0         defined $self->{token}
206             or croak
207             'token must be passed to ->new, or call ->login, ->register before this.';
208 0 0         defined $args->{project_id} or croak 'project_id is required.';
209              
210 0 0         my $resp = $self->{ua}->post(
    0          
    0          
211             'https://todoist.com/API/updateProject',
212             [
213             token => $self->{token},
214             project_id => $args->{project_id},
215             $args->{name} ? ( order => $args->{name} ) : (),
216             $args->{color} ? ( color => $args->{color} ) : (),
217             $args->{indent} ? ( indent => $args->{indent} ) : (),
218             ]
219             );
220 0 0         unless ( $resp->is_success ) {
221 0           $errstr = $resp->status_line;
222 0           return;
223             }
224 0 0         if ( $resp->content =~ 'ERROR_PROJECT_NOT_FOUND' ) {
225 0           $errstr = $resp->content;
226 0           return;
227             }
228              
229 0           my $data = $self->{json}->decode( $resp->content );
230 0           return $data;
231             }
232              
233             sub deleteProject {
234 0     0 1   my ( $self, $project_id ) = @_;
235              
236             # validate
237 0 0         defined $self->{token}
238             or croak
239             'token must be passed to ->new, or call ->login, ->register before this.';
240              
241 0           my $resp =
242             $self->{ua}->get(
243             "http://todoist.com/API/deleteProject?token=$self->{token}&project_id=$project_id"
244             );
245 0 0         unless ( $resp->is_success ) {
246 0           $errstr = $resp->status_line;
247 0           return;
248             }
249              
250 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
251             }
252              
253             sub getLabels {
254 0     0 1   my $self = shift;
255              
256             # validate
257 0 0         defined $self->{token}
258             or croak
259             'token must be passed to ->new, or call ->login, ->register before this.';
260              
261 0           my $resp =
262             $self->{ua}->get("http://todoist.com/API/getLabels?token=$self->{token}");
263 0 0         unless ( $resp->is_success ) {
264 0           $errstr = $resp->status_line;
265 0           return;
266             }
267              
268 0           my $data = $self->{json}->decode( $resp->content );
269 0 0         return wantarray ? @$data : $data;
270             }
271              
272             sub updateLabel {
273 0     0 1   my $self = shift;
274 0 0         my $args = scalar @_ % 2 ? shift : {@_};
275              
276             # validate
277 0 0         defined $self->{token}
278             or croak
279             'token must be passed to ->new, or call ->login, ->register before this.';
280 0 0         defined $args->{old_name} or croak 'old_name is required.';
281 0 0         defined $args->{new_name} or croak 'new_name is required.';
282              
283 0           my $resp = $self->{ua}->post(
284             'https://todoist.com/API/updateLabel',
285             [
286             token => $self->{token},
287             old_name => $args->{old_name},
288             new_name => $args->{new_name},
289             ]
290             );
291 0 0         unless ( $resp->is_success ) {
292 0           $errstr = $resp->status_line;
293 0           return;
294             }
295              
296 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
297             }
298              
299             sub deleteLabel {
300 0     0 1   my ( $self, $name ) = @_;
301              
302             # validate
303 0 0         defined $self->{token}
304             or croak
305             'token must be passed to ->new, or call ->login, ->register before this.';
306              
307 0           my $resp = $self->{ua}->get(
308             "http://todoist.com/API/deleteLabel?token=$self->{token}&name=$name");
309 0 0         unless ( $resp->is_success ) {
310 0           $errstr = $resp->status_line;
311 0           return;
312             }
313              
314 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
315             }
316              
317             sub getUncompletedItems {
318 0     0 1   my ( $self, $project_id, $js_date ) = @_;
319              
320             # validate
321 0 0         defined $self->{token}
322             or croak
323             'token must be passed to ->new, or call ->login, ->register before this.';
324              
325 0           my $url =
326             "http://todoist.com/API/getUncompletedItems?token=$self->{token}&project_id=$project_id";
327 0 0         $url .= '&js_date=1' if $js_date;
328 0           my $resp = $self->{ua}->get($url);
329 0 0         unless ( $resp->is_success ) {
330 0           $errstr = $resp->status_line;
331 0           return;
332             }
333              
334 0           my $data = $self->{json}->decode( $resp->content );
335 0 0         return wantarray ? @$data : $data;
336             }
337              
338             sub getCompletedItems {
339 0     0 1   my ( $self, $project_id, $js_date ) = @_;
340              
341             # validate
342 0 0         defined $self->{token}
343             or croak
344             'token must be passed to ->new, or call ->login, ->register before this.';
345              
346 0           my $url =
347             "http://todoist.com/API/getCompletedItems?token=$self->{token}&project_id=$project_id";
348 0 0         $url .= '&js_date=1' if $js_date;
349 0           my $resp = $self->{ua}->get($url);
350 0 0         unless ( $resp->is_success ) {
351 0           $errstr = $resp->status_line;
352 0           return;
353             }
354              
355 0           my $data = $self->{json}->decode( $resp->content );
356 0 0         return wantarray ? @$data : $data;
357             }
358              
359             sub getItemsById {
360 0     0 1   my ( $self, $item_ids, $js_date ) = @_;
361              
362             # validate
363 0 0         defined $self->{token}
364             or croak
365             'token must be passed to ->new, or call ->login, ->register before this.';
366              
367 0 0         $item_ids = [$item_ids] unless ref $item_ids eq 'ARRAY';
368              
369 0           my $url = "http://todoist.com/API/getItemsById?token=$self->{token}&ids="
370             . join( ',', @$item_ids );
371 0 0         $url .= '&js_date=1' if $js_date;
372 0           my $resp = $self->{ua}->get($url);
373 0 0         unless ( $resp->is_success ) {
374 0           $errstr = $resp->status_line;
375 0           return;
376             }
377              
378 0           my $data = $self->{json}->decode( $resp->content );
379 0 0         return wantarray ? @$data : $data;
380             }
381              
382             sub addItem {
383 0     0 1   my $self = shift;
384 0 0         my $args = scalar @_ % 2 ? shift : {@_};
385              
386             # validate
387 0 0         defined $self->{token}
388             or croak
389             'token must be passed to ->new, or call ->login, ->register before this.';
390 0 0         defined $args->{project_id} or croak 'project_id is required.';
391 0 0         defined $args->{content} or croak 'content is required.';
392              
393 0 0         my $resp = $self->{ua}->post(
    0          
    0          
394             'https://todoist.com/API/addItem',
395             [
396             token => $self->{token},
397             project_id => $args->{project_id},
398             content => $args->{content},
399             $args->{date_string} ? ( date_string => $args->{date_string} ) : (),
400             $args->{priority} ? ( priority => $args->{priority} ) : (),
401             $args->{js_date} ? ( js_date => $args->{js_date} ) : (),
402             ]
403             );
404 0 0         unless ( $resp->is_success ) {
405 0           $errstr = $resp->status_line;
406 0           return;
407             }
408 0 0         unless ( $resp->content =~ 'id' ) {
409 0           $errstr = $resp->content;
410 0           return;
411             }
412              
413 0           my $data = $self->{json}->decode( $resp->content );
414 0           return $data;
415             }
416              
417             sub updateItem {
418 0     0 1   my $self = shift;
419 0 0         my $args = scalar @_ % 2 ? shift : {@_};
420              
421             # validate
422 0 0         defined $self->{token}
423             or croak
424             'token must be passed to ->new, or call ->login, ->register before this.';
425 0 0         defined $args->{id} or croak 'id is required.';
426              
427 0 0         my $resp = $self->{ua}->post(
    0          
    0          
    0          
    0          
    0          
428             'https://todoist.com/API/updateItem',
429             [
430             token => $self->{token},
431             id => $args->{id},
432             $args->{content} ? ( content => $args->{content} ) : (),
433             $args->{date_string} ? ( date_string => $args->{date_string} ) : (),
434             $args->{priority} ? ( priority => $args->{priority} ) : (),
435             $args->{indent} ? ( indent => $args->{indent} ) : (),
436             $args->{item_order} ? ( item_order => $args->{item_order} ) : (),
437             $args->{js_date} ? ( js_date => $args->{js_date} ) : (),
438             ]
439             );
440 0 0         unless ( $resp->is_success ) {
441 0           $errstr = $resp->status_line;
442 0           return;
443             }
444 0 0         unless ( $resp->content =~ 'id' ) {
445 0           $errstr = $resp->content;
446 0           return;
447             }
448              
449 0           my $data = $self->{json}->decode( $resp->content );
450 0           return $data;
451             }
452              
453             sub updateOrders {
454 0     0 1   my ( $self, $project_id, $item_ids ) = @_;
455              
456             # validate
457 0 0         defined $self->{token}
458             or croak
459             'token must be passed to ->new, or call ->login, ->register before this.';
460 0 0         defined $project_id or croak 'project_id is required.';
461              
462 0 0         $item_ids = [$item_ids] unless ref $item_ids eq 'ARRAY';
463              
464 0           my $url =
465             "http://todoist.com/API/updateOrders?token=$self->{token}&project_id=$project_id&item_id_list=["
466             . join( ',', @$item_ids ) . ']';
467 0           my $resp = $self->{ua}->get($url);
468 0 0         unless ( $resp->is_success ) {
469 0           $errstr = $resp->status_line;
470 0           return;
471             }
472              
473 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
474             }
475              
476             sub updateRecurringDate {
477 0     0 1   my ( $self, $item_ids, $js_date ) = @_;
478              
479             # validate
480 0 0         defined $self->{token}
481             or croak
482             'token must be passed to ->new, or call ->login, ->register before this.';
483              
484 0 0         $item_ids = [$item_ids] unless ref $item_ids eq 'ARRAY';
485              
486 0           my $url =
487             "http://todoist.com/API/updateRecurringDate?token=$self->{token}&ids=["
488             . join( ',', @$item_ids ) . ']';
489 0 0         $url .= '&js_date=1' if $js_date;
490 0           my $resp = $self->{ua}->get($url);
491 0 0         unless ( $resp->is_success ) {
492 0           $errstr = $resp->status_line;
493 0           return;
494             }
495              
496 0           my $data = $self->{json}->decode( $resp->content );
497 0 0         return wantarray ? @$data : $data;
498             }
499              
500             sub deleteItems {
501 0     0 1   my ( $self, @item_ids ) = @_;
502              
503             # validate
504 0 0         defined $self->{token}
505             or croak
506             'token must be passed to ->new, or call ->login, ->register before this.';
507 0 0 0       @item_ids = @{ $item_ids[0] }
  0            
508             if scalar(@item_ids) == 1 and ref $item_ids[0] eq 'ARRAY';
509              
510 0           my $url = "http://todoist.com/API/deleteItems?token=$self->{token}&ids=["
511             . join( ',', @item_ids ) . ']';
512 0           my $resp = $self->{ua}->get($url);
513 0 0         unless ( $resp->is_success ) {
514 0           $errstr = $resp->status_line;
515 0           return;
516             }
517              
518 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
519             }
520              
521             sub completeItems {
522 0     0 1   my ( $self, $item_ids, $in_history ) = @_;
523              
524             # validate
525 0 0         defined $self->{token}
526             or croak
527             'token must be passed to ->new, or call ->login, ->register before this.';
528 0 0         $item_ids = [$item_ids] unless ref $item_ids eq 'ARRAY';
529              
530 0           my $url = "http://todoist.com/API/completeItems?token=$self->{token}&ids=["
531             . join( ',', @$item_ids ) . ']';
532 0 0         $url .= '&in_history=1' if $in_history;
533 0           my $resp = $self->{ua}->get($url);
534 0 0         unless ( $resp->is_success ) {
535 0           $errstr = $resp->status_line;
536 0           return;
537             }
538              
539 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
540             }
541              
542             sub uncompleteItems {
543 0     0 1   my ( $self, @item_ids ) = @_;
544              
545             # validate
546 0 0         defined $self->{token}
547             or croak
548             'token must be passed to ->new, or call ->login, ->register before this.';
549 0 0 0       @item_ids = @{ $item_ids[0] }
  0            
550             if scalar(@item_ids) == 1 and ref $item_ids[0] eq 'ARRAY';
551              
552 0           my $url =
553             "http://todoist.com/API/uncompleteItems?token=$self->{token}&ids=["
554             . join( ',', @item_ids ) . ']';
555 0           my $resp = $self->{ua}->get($url);
556 0 0         unless ( $resp->is_success ) {
557 0           $errstr = $resp->status_line;
558 0           return;
559             }
560              
561 0 0         return ( $resp->content =~ /ok/i ) ? 1 : 0;
562             }
563              
564             sub query {
565 0     0 1   my $self = shift;
566 0 0         my $args = scalar @_ % 2 ? shift : {@_};
567              
568             # validate
569 0 0         defined $self->{token}
570             or croak
571             'token must be passed to ->new, or call ->login, ->register before this.';
572 0 0         defined $args->{queries} or croak 'queries is required.';
573 0           my $queries = $args->{queries};
574 0 0         $queries = [$queries] unless ref $queries eq 'ARRAY';
575              
576 0 0         my $resp = $self->{ua}->post(
    0          
577             'https://todoist.com/API/query',
578             [
579             token => $self->{token},
580             queries => '[' . join( ',', @$queries ) . ']',
581             $args->{as_count} ? ( as_count => $args->{as_count} ) : (),
582             $args->{js_date} ? ( js_date => $args->{js_date} ) : (),
583             ]
584             );
585 0 0         unless ( $resp->is_success ) {
586 0           $errstr = $resp->status_line;
587 0           return;
588             }
589              
590 0           my $data = $self->{json}->decode( $resp->content );
591 0 0         return wantarray ? @$data : $data;
592             }
593              
594             1;
595              
596             __END__