WebService-PivotalTracker/lib/WebService/PivotalTracker/Story.pm
package WebService::PivotalTracker::Story;
use strict;
use warnings;
use namespace::autoclean;
our $VERSION = '0.12';
use Params::ValidationCompiler qw( validation_for );
use WebService::PivotalTracker::Comment;
use WebService::PivotalTracker::Label;
use WebService::PivotalTracker::Person;
use WebService::PivotalTracker::PropertyAttributes;
use WebService::PivotalTracker::Types qw(
ArrayRef CommentObject DateTimeObject LabelObject Maybe
NonEmptyStr Num PersonObject PositiveInt Str StoryState StoryType Uri
);
use Moo;
my %props = (
id => PositiveInt,
project_id => PositiveInt,
name => NonEmptyStr,
description => Str,
story_type => StoryType,
current_state => StoryState,
estimate => Maybe [Num],
accepted_at => {
type => DateTimeObject,
inflator => '_inflate_iso8601_datetime',
},
deadline => {
type => DateTimeObject,
inflator => '_inflate_iso8601_datetime',
},
requested_by_id => PositiveInt,
owner_ids => ArrayRef [PositiveInt],
task_ids => {
type => ArrayRef [PositiveInt],
default => sub { [] },
},
follower_ids => {
type => ArrayRef [PositiveInt],
default => sub { [] },
},
created_at => {
type => DateTimeObject,
inflator => '_inflate_iso8601_datetime',
},
updated_at => {
type => DateTimeObject,
inflator => '_inflate_iso8601_datetime',
},
url => {
type => Uri,
inflator => '_inflate_uri',
},
kind => NonEmptyStr,
);
has( @{$_} ) for props_to_attributes(%props);
has comments => (
is => 'ro',
isa => ArrayRef [CommentObject],
init_arg => undef,
lazy => 1,
builder => '_build_comments',
clearer => '_clear_comments',
);
has labels => (
is => 'ro',
isa => ArrayRef [LabelObject],
init_arg => undef,
lazy => 1,
builder => '_build_labels',
clearer => '_clear_labels',
);
has requested_by => (
is => 'ro',
isa => PersonObject,
lazy => 1,
default => sub {
my $self = shift;
WebService::PivotalTracker::Person->new(
raw_content => $self->raw_content->{requested_by},
pt_api => $self->_pt_api,
);
},
);
with 'WebService::PivotalTracker::Entity';
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _properties {
return %props;
}
## use critic
{
my $check = validation_for(
params => {
current_state => { type => StoryState },
}
);
sub update {
my $self = shift;
my %args = $check->(@_);
my $raw = $self->_client->put( $self->_self_uri, \%args );
return ( ref $self )->new(
raw_content => $raw,
pt_api => $self->_pt_api,
);
}
}
{
my $check = validation_for(
params => {
person_id => {
type => PositiveInt,
optional => 1,
},
text => { type => NonEmptyStr },
}
);
sub add_comment {
my $self = shift;
my %args = $check->(@_);
my $comment = WebService::PivotalTracker::Comment->new(
raw_content =>
$self->_client->post( $self->_comments_uri, \%args ),
pt_api => $self->_pt_api,
);
$self->_clear_comments;
return $comment;
}
}
# We could fetch each id in $self->comment_ids one at a time, but there's an
# endpoint to get all the comments at once, which is going to be more
# efficient.
sub _build_comments {
my $self = shift;
my $raw_comments = $self->_client->get( $self->_comments_uri );
return [
map {
WebService::PivotalTracker::Comment->new(
raw_content => $_,
pt_api => $self->_pt_api,
)
} @{$raw_comments}
];
}
sub _comments_uri {
my $self = shift;
my $path = sprintf(
'/projects/%s/stories/%s/comments',
$self->project_id,
$self->id,
);
return $self->_client->build_uri($path);
}
before _clear_comments => sub {
my $self = shift;
delete $self->raw_content->{comments};
};
{
my $check = validation_for(
params => {
name => { type => NonEmptyStr },
}
);
sub add_label {
my $self = shift;
my %args = $check->(@_);
$self->_client->post( $self->_labels_uri, \%args );
$self->_clear_labels;
return;
}
}
before _clear_labels => sub {
my $self = shift;
delete $self->raw_content->{labels};
};
# We might already have all the label info, otherwise we can fetch all the
# labels at once rather iterating over each id, just like with comments.
sub _build_labels {
my $self = shift;
if ( $self->raw_content->{labels} ) {
return [
map {
WebService::PivotalTracker::Label->new(
raw_content => $_,
pt_api => $self->_pt_api,
)
} @{ $self->raw_content->{labels} }
];
}
my $raw_labels = $self->_client->get( $self->_labels_uri );
return [
map {
WebService::PivotalTracker::Label->new(
raw_content => $_,
pt_api => $self->_pt_api,
)
} @{$raw_labels}
];
}
sub _labels_uri {
my $self = shift;
my $path = sprintf(
'/projects/%s/stories/%s/labels',
$self->project_id,
$self->id,
);
return $self->_client->build_uri($path);
}
sub _self_uri {
my $self = shift;
return $self->_client->build_uri(
sprintf(
'/stories/%s',
$self->id,
)
);
}
1;
# ABSTRACT: A single story
__END__
=pod
=encoding UTF-8
=head1 NAME
WebService::PivotalTracker::Story - A single story
=head1 VERSION
version 0.12
=head1 SYNOPSIS
=head1 DESCRIPTION
This class represents a single story.
=for Test::Synopsis my $project;
my $iterations = $project->stories( ... );
=head1 ATTRIBUTES
This class provides the following attribute accessor methods. Each one
corresponds to a property defined by the L<PT REST API V5 story resource
docs|https://www.pivotaltracker.com/help/api/rest/v5#story_resource>.
=head2 id
=head2 project_id
=head2 name
=head2 description
The description in Markdown.
=head2 story_type
=head2 current_state
=head2 estimate
=head2 accepted_at
This will be returned as a L<DateTime> object.
=head2 deadline
This will be returned as a L<DateTime> object.
=head2 requested_by_id
=head2 owner_ids
An array reference.
=head2 task_ids
An array reference.
=head2 follower_ids
An array reference.
=head2 created_at
This will be returned as a L<DateTime> object.
=head2 updated_at
This will be returned as a L<DateTime> object.
=head2 url
This will be returned as a L<URI> object.
=head2 kind
=head2 raw_content
The raw JSON used to create this object.
=head1 METHODS
This class provides the following methods:
=head2 $story->comments
This method returns an array reference of
L<WebService::PivotalTracker::Comment> objects.
=head2 $story->labels
This method returns an array reference of L<WebService::PivotalTracker::Label>
objects.
=head2 $story->requested_by
This method returns a L<WebService::PivotalTracker::Person> representing the
person who is the requester for the story.
=head2 $story->update( ... )
This method will update the story's properties as specified.
=head2 $story->add_comment( ... )
This method adds a comment to a story. It accepts two arguments:
=over 4
=item * text
The text of the comment in Markdown.
This is required.
=item * person_id
By default, the comment will be attributed to whoever owns the token, but you
can use this to override that.
=back
=head2 $story->add_label( ... )
This method accepts a single argument, C<name>, which is the name of the label
to add.
=head1 SUPPORT
Bugs may be submitted through L<https://github.com/maxmind/WebService-PivotalTracker/issues>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2016 - 2020 by MaxMind, Inc.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
=cut