File Coverage

blib/lib/RT/Client/REST/Ticket.pm
Criterion Covered Total %
statement 63 73 86.3
branch 8 12 66.6
condition 1 3 33.3
subroutine 20 24 83.3
pod 3 3 100.0
total 95 115 82.6


line stmt bran cond sub pod time code
1             #!perl
2             # vim: softtabstop=4 tabstop=4 shiftwidth=4 ft=perl expandtab smarttab
3             # PODNAME: RT::Client::REST::Ticket
4             # ABSTRACT: ticket object representation.
5              
6 2     2   95835 use strict;
  2         11  
  2         51  
7 2     2   8 use warnings;
  2         4  
  2         84  
8              
9             $RT::Client::REST::Ticket::VERSION = '0.70';
10             use parent 'RT::Client::REST::Object';
11 2     2   394  
  2         273  
  2         11  
12             use Try::Tiny;
13 2     2   140 use Params::Validate qw(:types);
  2         4  
  2         115  
14 2     2   11 use RT::Client::REST;
  2         3  
  2         394  
15 2     2   603 use RT::Client::REST::Attachment;
  2         4  
  2         58  
16 2     2   838 use RT::Client::REST::Object::Exception;
  2         5  
  2         59  
17 2     2   13 use RT::Client::REST::SearchResult;
  2         4  
  2         6  
18 2     2   88 use RT::Client::REST::Transaction;
  2         3  
  2         35  
19 2     2   720  
  2         5  
  2         674  
20              
21              
22             id => {
23             validation => {
24             type => SCALAR,
25             regex => qr/^\d+$/,
26             },
27             form2value => sub {
28             shift =~ m~^ticket/(\d+)$~i;
29             return $1;
30 0     0   0 },
31 0         0 value2form => sub {
32             return 'ticket/' . shift;
33             },
34 0     0   0 },
35              
36             queue => {
37             validation => {
38 2     2   105 type => SCALAR,
39             },
40             },
41              
42             owner => {
43             validation => {
44             type => SCALAR,
45             },
46             },
47              
48             creator => {
49             validation => {
50             type => SCALAR,
51             },
52             },
53              
54             subject => {
55             validation => {
56             type => SCALAR,
57             },
58             },
59              
60             status => {
61             validation => {
62             # That's it for validation... People can create their own
63             # custom statuses.
64             type => SCALAR,
65             },
66             rest_name => 'Status',
67             },
68              
69             priority => {
70             validation => {
71             type => SCALAR,
72             },
73             },
74              
75             initial_priority => {
76             validation => {
77             type => SCALAR,
78             },
79             rest_name => 'InitialPriority',
80             },
81              
82             final_priority => {
83             validation => {
84             type => SCALAR,
85             },
86             rest_name => 'FinalPriority',
87             },
88              
89             requestors => {
90             validation => {
91             type => ARRAYREF,
92             },
93             list => 1,
94             },
95              
96             requestor => {
97             validation => {
98             type => ARRAYREF,
99             },
100             list => 1,
101             },
102              
103             cc => {
104             validation => {
105             type => ARRAYREF,
106             },
107             list => 1,
108             },
109              
110             admin_cc => {
111             validation => {
112             type => ARRAYREF,
113             },
114             list => 1,
115             rest_name => 'AdminCc',
116             },
117              
118             created => {
119             validation => {
120             type => SCALAR,
121             },
122             is_datetime => 1,
123             },
124              
125             starts => {
126             validation => {
127             type => SCALAR|UNDEF,
128             },
129             is_datetime => 1,
130             },
131              
132             started => {
133             validation => {
134             type => SCALAR|UNDEF,
135             },
136             is_datetime => 1,
137             },
138              
139             due => {
140             validation => {
141             type => SCALAR|UNDEF,
142             },
143             is_datetime => 1,
144             },
145              
146             resolved => {
147             validation => {
148             type => SCALAR|UNDEF,
149             },
150             is_datetime => 1,
151             },
152              
153             told => {
154             validation => {
155             type => SCALAR|UNDEF,
156             },
157             is_datetime => 1,
158             },
159              
160             time_estimated => {
161             validation => {
162             type => SCALAR,
163             },
164             rest_name => 'TimeEstimated',
165             },
166              
167             time_worked => {
168             validation => {
169             type => SCALAR,
170             },
171             rest_name => 'TimeWorked',
172             },
173              
174             time_left => {
175             validation => {
176             type => SCALAR,
177             },
178             rest_name => 'TimeLeft',
179             },
180              
181             last_updated => {
182             validation => {
183             type => SCALAR,
184             },
185             rest_name => 'LastUpdated',
186             is_datetime => 1,
187             },
188              
189             sla => {
190             validation => {
191             type => SCALAR,
192             },
193             },
194              
195             }}
196              
197              
198             # comment and correspond are really the same method, so we save ourselves
199             # some duplication here.
200             for my $method (qw(comment correspond)) {
201             no strict 'refs'; ## no critic (ProhibitNoStrict)
202             *$method = sub {
203 2     2   13 my $self = shift;
  2         4  
  2         717  
204              
205 16     16   9737 if (@_ & 1) {
206             RT::Client::REST::Object::OddNumberOfArgumentsException->throw;
207 16 100       47 }
208 4         36  
209             $self->_assert_rt_and_id($method);
210              
211 12         47 my %opts = @_;
212              
213 8         23 unless (defined($opts{message})) {
214             RT::Client::REST::Object::InvalidValueException->throw(
215 8 100       17 "No message was provided",
216 4         13 );
217             }
218              
219             $self->rt->$method(
220             ticket_id => $self->id,
221 4         11 %opts,
222             );
223              
224             return;
225             };
226 0         0 }
227              
228              
229             my $self = shift;
230              
231             $self->_assert_rt_and_id;
232 3     3 1 638  
233             RT::Client::REST::SearchResult->new(
234 3         10 ids => [ $self->rt->get_attachment_ids(id => $self->id) ],
235             object => sub {
236             RT::Client::REST::Attachment->new(
237             id => shift,
238             parent_id => $self->id,
239 0     0   0 rt => $self->rt,
240             );
241             },
242             );
243             }
244              
245 1         415  
246             my $self = shift;
247              
248             if (@_ & 1) {
249             RT::Client::REST::Object::OddNumberOfArgumentsException->throw;
250 3     3 1 653 }
251              
252 3 50       11 $self->_assert_rt_and_id;
253 0         0  
254             my %opts = @_;
255             my %params = (
256 3         10 parent_id => $self->id,
257             );
258 1         3 if (defined(my $type = delete($opts{type}))) {
259 1         4 $params{transaction_type} = $type;
260             }
261              
262 1 50       5 RT::Client::REST::SearchResult->new(
263 0         0 ids => [ $self->rt->get_transaction_ids(%params) ],
264             object => sub {
265             RT::Client::REST::Transaction->new(
266             id => shift,
267             parent_id => $self->id,
268             rt => $self->rt,
269 0     0   0 );
270             },
271             );
272             }
273              
274              
275 1         4 for my $method (qw(take untake steal)) {
276             no strict 'refs'; ## no critic (ProhibitNoStrict)
277             *$method = sub {
278             my $self = shift;
279              
280 2     2   14 $self->_assert_rt_and_id($method);
  2         4  
  2         390  
281              
282 9     9   1966 try {
283             $self->rt->$method(id => $self->id);
284 9         30 }
285             catch {
286             die $_ unless blessed $_ && $_->can('rethrow');
287 3     3   305  
288             if ($_->isa('RT::Client::REST::AlreadyTicketOwnerException')) {
289             # Rename the exception.
290 3 50 33 3   3030 RT::Client::REST::Object::NoopOperationException
291             ->throw(shift->message);
292 3 50       28 }
293             else {
294 0         0 $_->rethrow;
295             }
296             };
297              
298 3         8 return;
299             };
300 3         43 }
301              
302 0         0  
303              
304              
305             __PACKAGE__->_generate_methods;
306              
307 1     1 1 5 1;
308              
309              
310             =pod
311              
312             =encoding UTF-8
313              
314             =head1 NAME
315              
316             RT::Client::REST::Ticket - ticket object representation.
317              
318             =head1 VERSION
319              
320             version 0.70
321              
322             =head1 SYNOPSIS
323              
324             my $rt = RT::Client::REST->new(server => $ENV{RTSERVER});
325              
326             # Create a new ticket:
327             my $ticket = RT::Client::REST::Ticket->new(
328             rt => $rt,
329             queue => "General",
330             subject => $subject,
331             )->store(text => "This is the initial text of the ticket");
332             print "Created a new ticket, ID ", $ticket->id, "\n";
333              
334             # Update
335             my $ticket = RT::Client::REST::Ticket->new(
336             rt => $rt,
337             id => $id,
338             priority => 10,
339             )->store;
340              
341             # Retrieve
342             my $ticket => RT::Client::REST::Ticket->new(
343             rt => $rt,
344             id => $id,
345             )->retrieve;
346              
347             unless ($ticket->owner eq $me) {
348             $ticket->steal; # Give me more work!
349             }
350              
351             =head1 DESCRIPTION
352              
353             B<RT::Client::REST::Ticket> is based on L<RT::Client::REST::Object>.
354             The representation allows one to retrieve, edit, comment on, and create
355             tickets in RT.
356              
357             =head1 ATTRIBUTES
358              
359             =over 2
360              
361             =item B<id>
362              
363             This is the numeric ID of the ticket.
364              
365             =item B<queue>
366              
367             This is the B<name> of the queue (not numeric id).
368              
369             =item B<owner>
370              
371             Username of the owner.
372              
373             =item B<creator>
374              
375             Username of RT user who created the ticket.
376              
377             =item B<subject>
378              
379             Subject of the ticket.
380              
381             =item B<status>
382              
383             The status is usually one of the following: "new", "open", "resolved",
384             "stalled", "rejected", and "deleted". However, custom RT installations
385             sometimes add their own statuses.
386              
387             =item B<priority>
388              
389             Ticket priority. Usually a numeric value.
390              
391             =item B<initial_priority>
392              
393             =item B<final_priority>
394              
395             =for stopwords requestor requestors
396              
397             =item B<requestor>
398              
399             This is the attribute for setting the requestor on ticket creation.
400             If you use requestors to do this in 3.8, the recipient may not receive
401             an auto-reply from RT because the ticket is initially created as the user
402             your REST session is connected as.
403              
404             It is a list attribute (for explanation of list attributes, see
405             B<LIST ATTRIBUTE PROPERTIES> in L<RT::Client::REST::Object>).
406              
407             =item B<requestors>
408              
409             This contains e-mail addresses of the requestors.
410              
411             It is a list attribute (for explanation of list attributes, see
412             B<LIST ATTRIBUTE PROPERTIES> in L<RT::Client::REST::Object>).
413              
414             =item B<cc>
415              
416             A list of e-mail addresses used to notify people of 'correspond'
417             actions.
418              
419             =item B<admin_cc>
420              
421             A list of e-mail addresses used to notify people of all actions performed
422             on a ticket.
423              
424             =item B<created>
425              
426             Time at which ticket was created. Note that this is an immutable field
427             and therefore the value cannot be changed..
428              
429             =item B<starts>
430              
431             =item B<started>
432              
433             =item B<due>
434              
435             =item B<resolved>
436              
437             =item B<told>
438              
439             =item B<time_estimated>
440              
441             =item B<time_worked>
442              
443             =item B<time_left>
444              
445             =item B<last_updated>
446              
447             =back
448              
449             =head2 Attributes storing a time
450              
451             The attributes which store a time stamp have an additional accessor with the
452             suffix C<_datetime> (e.g. C<resolved_datetime>). This allows you can get and
453             set the stored value as a DateTime object. Internally, it is converted into
454             the date-time string which RT uses, which is assumed to be in UTC.
455              
456             =head1 DB METHODS
457              
458             For full explanation of these, please see B<"DB METHODS"> in
459             L<RT::Client::REST::Object> documentation.
460              
461             =over 2
462              
463             =item B<retrieve>
464              
465             Retrieve RT ticket from database.
466              
467             =item B<store ([text =E<gt> $text])>
468              
469             Create or update the ticket. When creating a new ticket, optional 'text'
470             parameter can be supplied to set the initial text of the ticket.
471              
472             =item B<search>
473              
474             Search for tickets that meet specific conditions.
475              
476             =back
477              
478             =head1 TICKET-SPECIFIC METHODS
479              
480             =over 2
481              
482             =item B<comment> (message => $message, %opts)
483              
484             Comment on this ticket with message $message. C<%opts> is a list of
485             key-value pairs as follows:
486              
487             =over 2
488              
489             =item B<attachments>
490              
491             List of filenames (an array reference) that should be attached to the
492             ticket along with the comment.
493              
494             =item B<cc>
495              
496             List of e-mail addresses to send carbon copies to (an array reference).
497              
498             =for stopwords bcc
499              
500             =item B<bcc>
501              
502             List of e-mail addresses to send blind carbon copies to (an array
503             reference).
504              
505             =back
506              
507             =item B<correspond> (message => $message, %opts)
508              
509             Add correspondence to the ticket. Takes exactly the same arguments
510             as the B<comment> method above.
511              
512             =item B<attachments>
513              
514             Get attachments associated with this ticket. What is returned is an
515             object of type L<RT::Client::REST::SearchResult> which can then be used
516             to get at objects of type L<RT::Client::REST::Attachment>.
517              
518             =item B<transactions>
519              
520             Get transactions associated with this ticket. Optionally, you can specify
521             exactly what types of transactions you want listed, for example:
522              
523             my $result = $ticket->transactions(type => [qw(Comment Correspond)]);
524              
525             Please reference L<RT::Client::REST> documentation for the full list of
526             valid transaction types.
527              
528             Return value is an object of type L<RT::Client::REST::SearchResult> which
529             can then be used to iterate over transaction objects
530             (L<RT::Client::REST::Transaction>).
531              
532             =item B<take>
533              
534             Take this ticket.
535             If you already the owner of this ticket,
536             C<RT::Client::REST::Object::NoopOperationException> will be thrown.
537              
538             =for stopwords Untake untake
539              
540             =item B<untake>
541              
542             Untake this ticket.
543             If Nobody is already the owner of this ticket,
544             C<RT::Client::REST::Object::NoopOperationException> will be thrown.
545              
546             =item B<steal>
547              
548             Steal this ticket.
549             If you already the owner of this ticket,
550             C<RT::Client::REST::Object::NoopOperationException> will be thrown.
551              
552             =back
553              
554             =head1 CUSTOM FIELDS
555              
556             This class inherits 'cf' method from L<RT::Client::REST::Object>. To create
557             a ticket with a bunch of custom fields, use the following approach:
558              
559             RT::Client::REST::Ticket->new(
560             rt => $rt,
561             # blah blah
562             cf => {
563             'field one' => $value1,
564             'field two' => $another_value,
565             },
566             )->store;
567              
568             Some more examples:
569              
570             # Update a custom field value:
571             $ticket->cf('field one' => $value1);
572             $ticket->store;
573              
574             # Get a custom field value:
575             my $another value = $ticket->cf('field two');
576              
577             # Get a list of ticket's custom field names:
578             my @custom_fields = $ticket->cf;
579              
580             =head1 INTERNAL METHODS
581              
582             =over 2
583              
584             =item B<rt_type>
585              
586             Returns 'ticket'.
587              
588             =back
589              
590             =head1 SEE ALSO
591              
592             L<RT::Client::REST>, L<RT::Client::REST::Object>,
593             L<RT::Client::REST::Attachment>,
594             L<RT::Client::REST::SearchResult>,
595             L<RT::Client::REST::Transaction>.
596              
597             =head1 AUTHOR
598              
599             Dean Hamstead <dean@fragfest.com.au>
600              
601             =head1 COPYRIGHT AND LICENSE
602              
603             This software is copyright (c) 2022, 2020 by Dmitri Tikhonov.
604              
605             This is free software; you can redistribute it and/or modify it under
606             the same terms as the Perl 5 programming language system itself.
607              
608             =cut