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