Sponsoring The Perl Toolchain Summit 2025: Help make this important event another success Learn more

#!perl
# vim: softtabstop=4 tabstop=4 shiftwidth=4 ft=perl expandtab smarttab
# PODNAME: RT::Client::REST::Ticket
# ABSTRACT: ticket object representation.
use strict;
$RT::Client::REST::Ticket::VERSION = '0.72';
use Params::Validate qw(:types);
sub _attributes {{
id => {
validation => {
type => SCALAR,
regex => qr/^\d+$/,
},
form2value => sub {
shift =~ m~^ticket/(\d+)$~i;
return $1;
},
value2form => sub {
return 'ticket/' . shift;
},
},
queue => {
validation => {
type => SCALAR,
},
},
owner => {
validation => {
type => SCALAR,
},
},
creator => {
validation => {
type => SCALAR,
},
},
subject => {
validation => {
type => SCALAR,
},
},
status => {
validation => {
# That's it for validation... People can create their own
# custom statuses.
type => SCALAR,
},
rest_name => 'Status',
},
priority => {
validation => {
type => SCALAR,
},
},
initial_priority => {
validation => {
type => SCALAR,
},
rest_name => 'InitialPriority',
},
final_priority => {
validation => {
type => SCALAR,
},
rest_name => 'FinalPriority',
},
requestors => {
validation => {
type => ARRAYREF,
},
list => 1,
},
requestor => {
validation => {
type => ARRAYREF,
},
list => 1,
},
cc => {
validation => {
type => ARRAYREF,
},
list => 1,
},
admin_cc => {
validation => {
type => ARRAYREF,
},
list => 1,
rest_name => 'AdminCc',
},
created => {
validation => {
type => SCALAR,
},
is_datetime => 1,
},
starts => {
validation => {
type => SCALAR|UNDEF,
},
is_datetime => 1,
},
started => {
validation => {
type => SCALAR|UNDEF,
},
is_datetime => 1,
},
due => {
validation => {
type => SCALAR|UNDEF,
},
is_datetime => 1,
},
resolved => {
validation => {
type => SCALAR|UNDEF,
},
is_datetime => 1,
},
told => {
validation => {
type => SCALAR|UNDEF,
},
is_datetime => 1,
},
time_estimated => {
validation => {
type => SCALAR,
},
rest_name => 'TimeEstimated',
},
time_worked => {
validation => {
type => SCALAR,
},
rest_name => 'TimeWorked',
},
time_left => {
validation => {
type => SCALAR,
},
rest_name => 'TimeLeft',
},
last_updated => {
validation => {
type => SCALAR,
},
rest_name => 'LastUpdated',
is_datetime => 1,
},
sla => {
validation => {
type => SCALAR,
},
},
}}
# comment and correspond are really the same method, so we save ourselves
# some duplication here.
for my $method (qw(comment correspond)) {
no strict 'refs'; ## no critic (ProhibitNoStrict)
*$method = sub {
my $self = shift;
if (@_ & 1) {
RT::Client::REST::Object::OddNumberOfArgumentsException->throw;
}
$self->_assert_rt_and_id($method);
my %opts = @_;
unless (defined($opts{message})) {
RT::Client::REST::Object::InvalidValueException->throw(
"No message was provided",
);
}
$self->rt->$method(
ticket_id => $self->id,
%opts,
);
return;
};
}
sub attachments {
my $self = shift;
$self->_assert_rt_and_id;
RT::Client::REST::SearchResult->new(
ids => [ $self->rt->get_attachment_ids(id => $self->id) ],
object => sub {
RT::Client::REST::Attachment->new(
id => shift,
parent_id => $self->id,
rt => $self->rt,
);
},
);
}
sub transactions {
my $self = shift;
if (@_ & 1) {
RT::Client::REST::Object::OddNumberOfArgumentsException->throw;
}
$self->_assert_rt_and_id;
my %opts = @_;
my %params = (
parent_id => $self->id,
);
if (defined(my $type = delete($opts{type}))) {
$params{transaction_type} = $type;
}
RT::Client::REST::SearchResult->new(
ids => [ $self->rt->get_transaction_ids(%params) ],
object => sub {
RT::Client::REST::Transaction->new(
id => shift,
parent_id => $self->id,
rt => $self->rt,
);
},
);
}
for my $method (qw(take untake steal)) {
no strict 'refs'; ## no critic (ProhibitNoStrict)
*$method = sub {
my $self = shift;
$self->_assert_rt_and_id($method);
try {
$self->rt->$method(id => $self->id);
}
catch {
die $_ unless blessed $_ && $_->can('rethrow');
if ($_->isa('RT::Client::REST::AlreadyTicketOwnerException')) {
# Rename the exception.
RT::Client::REST::Object::NoopOperationException
->throw(shift->message);
}
else {
$_->rethrow;
}
};
return;
};
}
sub rt_type { 'ticket' }
__PACKAGE__->_generate_methods;
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
RT::Client::REST::Ticket - ticket object representation.
=head1 VERSION
version 0.72
=head1 SYNOPSIS
my $rt = RT::Client::REST->new(server => $ENV{RTSERVER});
# Create a new ticket:
my $ticket = RT::Client::REST::Ticket->new(
rt => $rt,
queue => "General",
subject => $subject,
)->store(text => "This is the initial text of the ticket");
print "Created a new ticket, ID ", $ticket->id, "\n";
# Update
my $ticket = RT::Client::REST::Ticket->new(
rt => $rt,
id => $id,
priority => 10,
)->store;
# Retrieve
my $ticket => RT::Client::REST::Ticket->new(
rt => $rt,
id => $id,
)->retrieve;
unless ($ticket->owner eq $me) {
$ticket->steal; # Give me more work!
}
=head1 DESCRIPTION
B<RT::Client::REST::Ticket> is based on L<RT::Client::REST::Object>.
The representation allows one to retrieve, edit, comment on, and create
tickets in RT.
=head1 ATTRIBUTES
=over 2
=item B<id>
This is the numeric ID of the ticket.
=item B<queue>
This is the B<name> of the queue (not numeric id).
=item B<owner>
Username of the owner.
=item B<creator>
Username of RT user who created the ticket.
=item B<subject>
Subject of the ticket.
=item B<status>
The status is usually one of the following: "new", "open", "resolved",
"stalled", "rejected", and "deleted". However, custom RT installations
sometimes add their own statuses.
=item B<priority>
Ticket priority. Usually a numeric value.
=item B<initial_priority>
=item B<final_priority>
=for stopwords requestor requestors
=item B<requestor>
This is the attribute for setting the requestor on ticket creation.
If you use requestors to do this in 3.8, the recipient may not receive
an auto-reply from RT because the ticket is initially created as the user
your REST session is connected as.
It is a list attribute (for explanation of list attributes, see
B<LIST ATTRIBUTE PROPERTIES> in L<RT::Client::REST::Object>).
=item B<requestors>
This contains e-mail addresses of the requestors.
It is a list attribute (for explanation of list attributes, see
B<LIST ATTRIBUTE PROPERTIES> in L<RT::Client::REST::Object>).
=item B<cc>
A list of e-mail addresses used to notify people of 'correspond'
actions.
=item B<admin_cc>
A list of e-mail addresses used to notify people of all actions performed
on a ticket.
=item B<created>
Time at which ticket was created. Note that this is an immutable field
and therefore the value cannot be changed..
=item B<starts>
=item B<started>
=item B<due>
=item B<resolved>
=item B<told>
=item B<time_estimated>
=item B<time_worked>
=item B<time_left>
=item B<last_updated>
=back
=head2 Attributes storing a time
The attributes which store a time stamp have an additional accessor with the
suffix C<_datetime> (e.g. C<resolved_datetime>). This allows you can get and
set the stored value as a DateTime object. Internally, it is converted into
the date-time string which RT uses, which is assumed to be in UTC.
=head1 DB METHODS
For full explanation of these, please see B<"DB METHODS"> in
L<RT::Client::REST::Object> documentation.
=over 2
=item B<retrieve>
Retrieve RT ticket from database.
=item B<store ([text =E<gt> $text])>
Create or update the ticket. When creating a new ticket, optional 'text'
parameter can be supplied to set the initial text of the ticket.
=item B<search>
Search for tickets that meet specific conditions.
=back
=head1 TICKET-SPECIFIC METHODS
=over 2
=item B<comment> (message => $message, %opts)
Comment on this ticket with message $message. C<%opts> is a list of
key-value pairs as follows:
=over 2
=item B<attachments>
List of filenames (an array reference) that should be attached to the
ticket along with the comment.
=item B<cc>
List of e-mail addresses to send carbon copies to (an array reference).
=for stopwords bcc
=item B<bcc>
List of e-mail addresses to send blind carbon copies to (an array
reference).
=back
=item B<correspond> (message => $message, %opts)
Add correspondence to the ticket. Takes exactly the same arguments
as the B<comment> method above.
=item B<attachments>
Get attachments associated with this ticket. What is returned is an
object of type L<RT::Client::REST::SearchResult> which can then be used
to get at objects of type L<RT::Client::REST::Attachment>.
=item B<transactions>
Get transactions associated with this ticket. Optionally, you can specify
exactly what types of transactions you want listed, for example:
my $result = $ticket->transactions(type => [qw(Comment Correspond)]);
Please reference L<RT::Client::REST> documentation for the full list of
valid transaction types.
Return value is an object of type L<RT::Client::REST::SearchResult> which
can then be used to iterate over transaction objects
(L<RT::Client::REST::Transaction>).
=item B<take>
Take this ticket.
If you already the owner of this ticket,
C<RT::Client::REST::Object::NoopOperationException> will be thrown.
=for stopwords Untake untake
=item B<untake>
Untake this ticket.
If Nobody is already the owner of this ticket,
C<RT::Client::REST::Object::NoopOperationException> will be thrown.
=item B<steal>
Steal this ticket.
If you already the owner of this ticket,
C<RT::Client::REST::Object::NoopOperationException> will be thrown.
=back
=head1 CUSTOM FIELDS
This class inherits 'cf' method from L<RT::Client::REST::Object>. To create
a ticket with a bunch of custom fields, use the following approach:
RT::Client::REST::Ticket->new(
rt => $rt,
# blah blah
cf => {
'field one' => $value1,
'field two' => $another_value,
},
)->store;
Some more examples:
# Update a custom field value:
$ticket->cf('field one' => $value1);
$ticket->store;
# Get a custom field value:
my $another value = $ticket->cf('field two');
# Get a list of ticket's custom field names:
my @custom_fields = $ticket->cf;
=head1 INTERNAL METHODS
=over 2
=item B<rt_type>
Returns 'ticket'.
=back
=head1 SEE ALSO
L<RT::Client::REST>, L<RT::Client::REST::Object>,
L<RT::Client::REST::Attachment>,
L<RT::Client::REST::SearchResult>,
L<RT::Client::REST::Transaction>.
=head1 AUTHOR
Dean Hamstead <dean@fragfest.com.au>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2023, 2020 by Dmitri Tikhonov.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut