File Coverage

blib/lib/WebService/PivotalTracker.pm
Criterion Covered Total %
statement 48 87 55.1
branch 1 4 25.0
condition 0 3 0.0
subroutine 16 23 69.5
pod 6 7 85.7
total 71 124 57.2


line stmt bran cond sub pod time code
1             package WebService::PivotalTracker;
2              
3 1     1   329695 use strict;
  1         10  
  1         24  
4 1     1   4 use warnings;
  1         2  
  1         20  
5 1     1   356 use namespace::autoclean;
  1         1269  
  1         3  
6              
7             our $VERSION = '0.12';
8              
9 1     1   423 use DateTime::Format::RFC3339;
  1         376960  
  1         32  
10 1     1   8 use Params::ValidationCompiler qw( validation_for );
  1         2  
  1         51  
11 1     1   5 use Scalar::Util qw( blessed );
  1         2  
  1         32  
12 1     1   459 use WebService::PivotalTracker::Client;
  1         4  
  1         32  
13 1     1   450 use WebService::PivotalTracker::Me;
  1         5  
  1         31  
14 1     1   460 use WebService::PivotalTracker::Project;
  1         4  
  1         39  
15 1     1   571 use WebService::PivotalTracker::ProjectIteration;
  1         4  
  1         36  
16 1     1   545 use WebService::PivotalTracker::ProjectMembership;
  1         3  
  1         38  
17 1     1   9 use WebService::PivotalTracker::Story;
  1         2  
  1         26  
18             use WebService::PivotalTracker::Types
19 1     1   6 qw( ArrayRef ClientObject IterationScope LWPObject MD5Hex NonEmptyStr PositiveInt Uri );
  1         2  
  1         9  
20              
21 1     1   1466 use Moo;
  1         3  
  1         6  
22              
23             has token => (
24             is => 'ro',
25             isa => MD5Hex,
26             required => 1,
27             );
28              
29             has base_uri => (
30             is => 'ro',
31             isa => Uri,
32             coerce => 1,
33             default => 'https://www.pivotaltracker.com/services/v5',
34             );
35              
36             has _ua => (
37             is => 'ro',
38             isa => LWPObject,
39             init_arg => 'ua',
40             predicate => '_has_ua',
41             );
42              
43             has _client => (
44             is => 'ro',
45             isa => ClientObject,
46             lazy => 1,
47             builder => '_build_client',
48             );
49              
50             # This is gross. Is there a better way to manage this? This is the only way to
51             # get the Person who is the requester. There is no person endpoint from which
52             # to retrieve this information later!
53             my $StoryFields = do {
54             ## no critic (Subroutines::ProtectPrivateSubs)
55             my %props = WebService::PivotalTracker::Story->_properties;
56             join ',', sort ( keys %props, 'requested_by' );
57             };
58              
59             sub projects {
60 0     0 1 0 my $self = shift;
61              
62 0         0 my $uri = $self->_client->build_uri('/projects');
63              
64             return [
65             map {
66 0         0 WebService::PivotalTracker::Project->new(
67             raw_content => $_,
68             pt_api => $self,
69             )
70 0         0 } @{ $self->_client->get($uri) }
  0         0  
71             ];
72             }
73              
74             {
75             my $check = validation_for(
76             params => {
77             project_id => { type => PositiveInt },
78             filter => {
79             type => NonEmptyStr,
80             optional => 1
81             },
82             }
83             );
84              
85             sub project_stories_where {
86 0     0 1 0 my $self = shift;
87 0         0 my %args = $check->(@_);
88              
89 0         0 my $uri = $self->_client->build_uri(
90             "/projects/$args{project_id}/stories?",
91             {
92             %args,
93             fields => $StoryFields,
94             },
95             );
96              
97             return [
98             map {
99 0         0 WebService::PivotalTracker::Story->new(
100             raw_content => $_,
101             pt_api => $self,
102             )
103 0         0 } @{ $self->_client->get($uri) }
  0         0  
104             ];
105             }
106             }
107              
108             {
109             my $check = validation_for(
110             params => {
111             story_id => { type => PositiveInt },
112             }
113             );
114              
115             sub story {
116 3     3 1 7854 my $self = shift;
117 3         56 my %args = $check->(@_);
118              
119 3         135 my $content = $self->_client->get(
120             $self->_client->build_uri(
121             "/stories/$args{story_id}",
122             { fields => $StoryFields }
123             ),
124             );
125 1         50 WebService::PivotalTracker::Story->new(
126             raw_content => $content,
127             pt_api => $self,
128             );
129             }
130             }
131              
132             {
133             my $check = validation_for(
134             params => {
135             project_id => { type => PositiveInt },
136             sort_by => {
137             type => NonEmptyStr, # This could be an enum.
138             optional => 1,
139             },
140             },
141             );
142              
143             sub project_memberships {
144 0     0 1 0 my $self = shift;
145 0         0 my %args = $check->(@_);
146              
147 0         0 my $project_id = delete $args{project_id};
148 0         0 my $uri = $self->_client->build_uri(
149             "/projects/$project_id/memberships",
150             \%args,
151             );
152              
153             return [
154             map {
155 0         0 WebService::PivotalTracker::ProjectMembership->new(
156             raw_content => $_,
157             pt_api => $self,
158             )
159 0         0 } @{ $self->_client->get($uri) }
  0         0  
160             ];
161             }
162             }
163              
164             {
165             my $check = validation_for(
166             params => {
167             project_id => { type => PositiveInt },
168             label => {
169             type => NonEmptyStr,
170             optional => 1
171             },
172             limit => {
173             type => PositiveInt,
174             default => 1,
175             },
176             offset => {
177             type => PositiveInt,
178             optional => 1,
179             },
180             scope => {
181             type => IterationScope,
182             optional => 1
183             },
184             },
185             );
186              
187             sub project_iterations {
188 0     0 0 0 my $self = shift;
189 0         0 my %args = $check->(@_);
190              
191 0         0 my $uri = $self->_client->build_uri(
192             "/projects/$args{project_id}/iterations",
193             \%args,
194             );
195              
196             return [
197             map {
198 0         0 WebService::PivotalTracker::ProjectIteration->new(
199             raw_content => $_,
200             pt_api => $self,
201             )
202 0         0 } @{ $self->_client->get($uri) }
  0         0  
203             ];
204             }
205             }
206              
207             # XXX - if we want to add more create_X methods we should find a way to
208             # streamline & simplify this code so we don't have to repeat this sort of
209             # boilerplate over and over. Maybe each entity class should provide more
210             # detail about the properties, including type, coercions (like DateTime ->
211             # RFC3339 string), required for create/update, etc.
212             {
213             ## no critic (Subroutines::ProtectPrivateSubs)
214             my %props = WebService::PivotalTracker::Story->_properties;
215             my %params = map {
216             $_ => blessed $props{$_}
217             ? { type => $props{$_} }
218             : { type => $props{$_}{type} }
219             } keys %props;
220              
221             my %required = map { $_ => 1 } qw( project_id name );
222             $params{$_}{optional} = 1 for grep { !$required{$_} } keys %props;
223              
224             %params = (
225             %params,
226             before_id => {
227             type => PositiveInt,
228             optional => 1,
229             },
230             after_id => {
231             type => PositiveInt,
232             optional => 1,
233             },
234             labels => {
235             type => ArrayRef [NonEmptyStr],
236             optional => 1
237             },
238             );
239              
240             my $check = validation_for(
241             params => \%params,
242             );
243              
244             sub create_story {
245 0     0 1 0 my $self = shift;
246 0         0 my %args = $check->(@_);
247              
248 0         0 $self->_deflate_datetime_values( \%args );
249              
250 0         0 my $project_id = delete $args{project_id};
251 0         0 my $raw_content = $self->_client->post(
252             $self->_client->build_uri("/projects/$project_id/stories"),
253             \%args,
254             );
255              
256 0         0 return WebService::PivotalTracker::Story->new(
257             raw_content => $raw_content,
258             pt_api => $self,
259             );
260             }
261             }
262              
263             sub me {
264 0     0 1 0 my $self = shift;
265              
266 0         0 return WebService::PivotalTracker::Me->new(
267             raw_content =>
268             $self->_client->get( $self->_client->build_uri('/me') ),
269             pt_api => $self,
270             );
271             }
272              
273             sub _build_client {
274 3     3   16395 my $self = shift;
275              
276 3 50       68 return WebService::PivotalTracker::Client->new(
277             token => $self->token,
278             base_uri => $self->base_uri,
279             ( $self->_has_ua ? ( ua => $self->_ua ) : () ),
280             );
281             }
282              
283             sub _deflate_datetime_values {
284 0     0     my $self = shift;
285 0           my $args = shift;
286              
287 0           for my $key ( keys %{$args} ) {
  0            
288 0 0 0       next unless blessed $args->{$key} && $args->{$key}->isa('DateTime');
289             $args->{$key}
290 0           = DateTime::Format::RFC3339->format_datetime( $args->{$key} );
291             }
292              
293 0           return;
294             }
295              
296             1;
297              
298             # ABSTRACT: Perl library for the Pivotal Tracker REST API
299              
300             __END__
301              
302             =pod
303              
304             =encoding UTF-8
305              
306             =head1 NAME
307              
308             WebService::PivotalTracker - Perl library for the Pivotal Tracker REST API
309              
310             =head1 VERSION
311              
312             version 0.12
313              
314             =head1 SYNOPSIS
315              
316             my $pt = WebService::PivotalTracker->new(
317             token => '...',
318             );
319             my $story = $pt->story( story_id => 1234 );
320             my $me = $pt->me;
321              
322             for my $label ( $story->labels ) { }
323              
324             for my $comment ( $story->comments ) { }
325              
326             =head1 DESCRIPTION
327              
328             B<This is fairly alpha software. The API is likely to change in breaking ways
329             in the future.>
330              
331             This module provides a Perl interface to the L<REST API
332             V5|https://www.pivotaltracker.com/help/api/rest/v5> for L<Pivotal
333             Tracker|https://www.pivotaltracker.com/>. You will need to refer to the L<REST
334             API docs|https://www.pivotaltracker.com/help/api/rest/v5> for some details, as
335             this documentation does not reproduce the details of every attribute available
336             for a resource.
337              
338             This class, C<WebService::PivotalTracker>, provides the main entry point for
339             all API calls.
340              
341             =for Pod::Coverage project_iterations
342              
343             =head1 METHODS
344              
345             All web requests which return anything other than a success status result in a
346             call to C<die> with a simple string error message. This will probably change
347             to something more useful in the future.
348              
349             This class provides the following methods:
350              
351             =head2 WebService::PivotalTracker->new(...)
352              
353             This creates a new object of this class. It accepts the following arguments:
354              
355             =over 4
356              
357             =item * token
358              
359             An MD5 access token for Pivotal Tracker. May be provided as a string or
360             something that stringifies to the token.
361              
362             This is required.
363              
364             =item * base_uri
365              
366             The base URI against which all requests will be made. This defaults to
367             C<https://www.pivotaltracker.com/services/v5>.
368              
369             =back
370              
371             =head2 $pt->projects
372              
373             This method returns an array reference of
374             L<WebService::PivotalTracker::Project> objects, one for each project to which
375             the token provides access.
376              
377             =head2 $pt->project_stories_where(...)
378              
379             This method accepts the following arguments:
380              
381             =over 4
382              
383             =item * story_id
384              
385             The id of the project you are querying.
386              
387             This is required.
388              
389             =item * filter
390              
391             A search filter. This is the same syntax as you would use in the PT
392             application for searching. See
393             L<https://www.pivotaltracker.com/help/articles/advanced_search/> for details.
394              
395             =back
396              
397             =head2 $pt->story(...)
398              
399             This method returns a single L<WebService::PivotalTracker::Story> object, if
400             one exists for the given id.
401              
402             This method accepts the following arguments:
403              
404             =over 4
405              
406             =item * story_id
407              
408             The id of the story you are querying.
409              
410             This is required.
411              
412             =back
413              
414             =head2 $pt->create_story(...)
415              
416             This creates a new story. This method accepts every attribute of a
417             L<WebService::PivotalTracker::Story> object. The C<project_id> and C<name>
418             parameters are required.
419              
420             It also accepts two additional optional parameters:
421              
422             =over 4
423              
424             =item * before_id
425              
426             A story ID before which this story should be added.
427              
428             =item * after_id
429              
430             A story ID after which this story should be added.
431              
432             =back
433              
434             By default the story will be added as the last story in the icebox.
435              
436             =head2 $pt->project_memberships(...)
437              
438             This looks up memberships in a project. It returns an array reference of
439             L<WebService::PivotalTracker::ProjectMembership> objects.
440              
441             It is useful if you need to discover information about a person who is a member
442             of your project.
443              
444             The C<project_id> parameter is required.
445              
446             The C<sort_by> parameter is optional.
447              
448             =head2 $pt->me
449              
450             This returns a L<WebService::PivotalTracker::Me> object for the user to which
451             the token belongs.
452              
453             =head1 SUPPORT
454              
455             Bugs may be submitted through L<https://github.com/maxmind/WebService-PivotalTracker/issues>.
456              
457             =head1 AUTHOR
458              
459             Dave Rolsky <autarch@urth.org>
460              
461             =head1 CONTRIBUTORS
462              
463             =for stopwords Dave Rolsky Florian Ragwitz Greg Oschwald William Storey
464              
465             =over 4
466              
467             =item *
468              
469             Dave Rolsky <drolsky@maxmind.com>
470              
471             =item *
472              
473             Florian Ragwitz <rafl@debian.org>
474              
475             =item *
476              
477             Greg Oschwald <goschwald@maxmind.com>
478              
479             =item *
480              
481             William Storey <wstorey@maxmind.com>
482              
483             =back
484              
485             =head1 COPYRIGHT AND LICENSE
486              
487             This software is Copyright (c) 2016 - 2020 by MaxMind, Inc.
488              
489             This is free software, licensed under:
490              
491             The Artistic License 2.0 (GPL Compatible)
492              
493             =cut