[Bps-public-commit] rt-extension-rest2 branch, tickets-bulk-message, created. 1.12-4-g6e105df
? sunnavy
sunnavy at bestpractical.com
Thu Jun 17 15:44:09 EDT 2021
The branch, tickets-bulk-message has been created
at 6e105df77e4d5a182c2e24d64c5bc3430df7e2a4 (commit)
- Log -----------------------------------------------------------------
commit 74939689042fa958a14895ad637e13c99fc7832b
Author: sunnavy <sunnavy at bestpractical.com>
Date: Thu Jun 17 23:57:24 2021 +0800
Abstract _add_message to return results instead of touching http response
Thus we can reuse it in TicketsBulk later.
Note that "CustomRoles" argument conversion is moved before input
validation: as custom role arguments are like "RT::CustomRole-1": "root"
in web UI(/Ticket/Update.html), the validation code customized in web UI
is more compatible in REST2 with this change.
diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index 48ffbf6..4421446 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -106,26 +106,43 @@ sub from_json {
sub add_message {
my $self = shift;
my %args = @_;
+
+ my ( $return_code, @results ) = $self->_add_message(%args);
+ if ( $return_code != 201 ) {
+ return error_as_json( $self->response, \$return_code, join "\n", @results );
+ }
+
+ $self->response->body( JSON::to_json( \@results, { pretty => 1 } ) );
+ return 1;
+}
+
+sub _add_message {
+ my $self = shift;
+ my %args = @_;
my @results;
- my $MIME = HTML::Mason::Commands::MakeMIMEEntity(
- Interface => 'REST',
- $args{NoContent} ? () : (Body => $args{Content} || $self->request->content),
- Type => $args{ContentType} || $self->request->content_type,
- Subject => $args{Subject},
- );
+ # update_role_members wants custom role IDs (like RT::CustomRole-ID)
+ # rather than role names.
+ %args = ( %args, %{ fix_custom_role_ids( $self->record, $args{CustomRoles} ) } ) if $args{CustomRoles};
# Check for any bad input data before making updates
my ($ok, $errmsg, $return_code) = $self->validate_input(\%args);
if (!$ok) {
if ( $return_code ) {
- return error_as_json($self->response, \$return_code, $errmsg);
+ return ($return_code, $errmsg);
}
else {
- return error_as_json($self->response, \400, $errmsg);
+ return (400, $errmsg);
}
}
+ my $MIME = HTML::Mason::Commands::MakeMIMEEntity(
+ Interface => 'REST',
+ $args{NoContent} ? () : (Body => $args{Content} || $self->request->content),
+ Type => $args{ContentType} || $self->request->content_type,
+ Subject => $args{Subject},
+ );
+
# Process attachments
foreach my $attachment (@{$args{Attachments}}) {
$MIME->attach(
@@ -149,22 +166,19 @@ sub add_message {
);
}
else {
- return \400;
+ push @results, $self->current_user->loc('Unknown type');
+ return ( 400, @results );
}
if (!$Trans) {
- return error_as_json(
- $self->response,
- \400, $msg || "Message failed for unknown reason");
+ push @results, $msg || $self->current_user->loc("Message failed for unknown reason");
+ return ( 400, @results );
}
push @results, $msg;
push @results, update_custom_fields($self->record, $args{CustomFields});
- # update_role_members wants custom role IDs (like RT::CustomRole-ID)
- # rather than role names.
- my $renamed_custom_roles = fix_custom_role_ids($self->record, $args{CustomRoles});
- push @results, update_role_members($self->record, $renamed_custom_roles);
+ push @results, update_role_members($self->record, \%args);
push @results, $self->_update_txn_custom_fields( $TransObj, $args{TxnCustomFields} || $args{TransactionCustomFields} );
# Set ticket status if we were passed a "Status":"foo" argument
@@ -174,9 +188,8 @@ sub add_message {
}
$self->created_transaction($TransObj);
- $self->response->body(JSON::to_json(\@results, { pretty => 1 }));
- return 1;
+ return ( 201, @results );
}
sub _update_txn_custom_fields {
commit ac978561ef14b8907973f6ec7c6fba1ebfb519c7
Author: sunnavy <sunnavy at bestpractical.com>
Date: Fri Jun 18 02:24:40 2021 +0800
Add /tickets/bulk/correspond and /tickets/bulk/comment endpoints
diff --git a/lib/RT/Extension/REST2/Resource/TicketsBulk.pm b/lib/RT/Extension/REST2/Resource/TicketsBulk.pm
index c9f1f2a..8194c96 100644
--- a/lib/RT/Extension/REST2/Resource/TicketsBulk.pm
+++ b/lib/RT/Extension/REST2/Resource/TicketsBulk.pm
@@ -14,9 +14,18 @@ use RT::Extension::REST2::Resource::Ticket;
use JSON ();
sub dispatch_rules {
- Path::Dispatcher::Rule::Regex->new( regex => qr{^/tickets/bulk/?$} );
+ Path::Dispatcher::Rule::Regex->new( regex => qr{^/tickets/bulk/?$} ),
+ Path::Dispatcher::Rule::Regex->new(
+ regex => qr{^/tickets/bulk/(correspond|comment)$},
+ block => sub { { type => shift->pos(1) } },
+ )
}
+has type => (
+ is => 'ro',
+ isa => 'Str',
+);
+
sub post_is_create { 1 }
sub create_path { '/tickets/bulk' }
sub charsets_provided { [ 'utf-8' ] }
@@ -52,17 +61,60 @@ sub from_json {
}
else {
for my $param ( @$params ) {
- my $resource = RT::Extension::REST2::Resource::Ticket->new(
- request => $self->request,
- response => $self->response,
- record_class => 'RT::Ticket',
- );
- my ( $ok, $msg ) = $resource->create_record( $param );
- if ( ref( $ok ) || !$ok ) {
- push @results, { message => $msg || "Create failed for unknown reason" };
+ if ( $self->type ) {
+ my $id = delete $param->{id};
+ if ( $id && $id =~ /^\d+$/ ) {
+ my $ticket = RT::Ticket->new($self->current_user);
+ $ticket->Load($id);
+ my $resource = RT::Extension::REST2::Resource::Message->new(
+ request => $self->request,
+ response => $self->response,
+ type => $self->type,
+ record => $ticket,
+ );
+
+ my @errors;
+
+ # Ported from RT::Extension::REST2::Resource::Message::from_json
+ if ( $param->{Attachments} ) {
+ foreach my $attachment ( @{ $param->{Attachments} } ) {
+ foreach my $field ( 'FileName', 'FileType', 'FileContent' ) {
+ push @errors, "$field is a required field for each attachment in Attachments"
+ unless $attachment->{$field};
+ }
+ }
+ }
+
+ $param->{NoContent} = 1 unless $param->{Content};
+ if ( !$param->{NoContent} && !$param->{ContentType} ) {
+ push @errors, "ContentType is a required field for application/json";
+ }
+
+ if (@errors) {
+ push @results, [ $id, @errors ];
+ next;
+ }
+
+ my ( $return_code, @messages ) = $resource->_add_message(%$param);
+ push @results, [ $id, @messages ];
+ }
+ else {
+ push @results, [ $id, 'Resource does not exist' ];
+ }
}
else {
- push @results, expand_uid( $resource->record->UID );
+ my $resource = RT::Extension::REST2::Resource::Ticket->new(
+ request => $self->request,
+ response => $self->response,
+ record_class => 'RT::Ticket',
+ );
+ my ( $ok, $msg ) = $resource->create_record($param);
+ if ( ref($ok) || !$ok ) {
+ push @results, { message => $msg || "Create failed for unknown reason" };
+ }
+ else {
+ push @results, expand_uid( $resource->record->UID );
+ }
}
}
}
commit 96b713a6cba048ae390a3209d8f77b1a0842175f
Author: sunnavy <sunnavy at bestpractical.com>
Date: Fri Jun 18 02:47:35 2021 +0800
Test /tickets/bulk/correspond and /tickets/bulk/comment endpoints
diff --git a/xt/tickets-bulk.t b/xt/tickets-bulk.t
index b6a67bf..0b8b23b 100644
--- a/xt/tickets-bulk.t
+++ b/xt/tickets-bulk.t
@@ -176,6 +176,7 @@ my @ticket_ids;
],
'json response content'
);
+ $user->PrincipalObj->RevokeRight( Right => 'ModifyTicket' );
}
{
@@ -185,5 +186,80 @@ my @ticket_ids;
}
}
+$user->PrincipalObj->GrantRight( Right => 'ShowTicket' );
+
+{
+ diag "no ReplyToTicket right";
+ my $res = $mech->post_json(
+ "$rest_base_path/tickets/bulk/correspond",
+ [ { id => $ticket_ids[0], Content => 'test correspond', ContentType => 'text/plain' } ],
+ 'Authorization' => $auth,
+ );
+ is( $res->code, 201, "status code" );
+ is_deeply( $mech->json_response, [ [ $ticket_ids[0], "Permission Denied", ] ], 'permission denied' );
+
+ diag "grant ReplyToTicket right";
+ $user->PrincipalObj->GrantRight( Right => 'ReplyToTicket' );
+
+ $res = $mech->post_json(
+ "$rest_base_path/tickets/bulk/correspond",
+ [ { id => $ticket_ids[0], Content => 'test correspond', ContentType => 'text/plain' } ],
+ 'Authorization' => $auth,
+ );
+ is( $res->code, 201, 'status code' );
+ is_deeply( $mech->json_response, [ [ $ticket_ids[0], "Correspondence added", ] ], 'Correspondence added' );
+
+ $user->PrincipalObj->GrantRight( Right => 'ModifyTicket' );
+ $res = $mech->post_json(
+ "$rest_base_path/tickets/bulk/correspond",
+ [ { id => $ticket_ids[0], Content => 'test correspond', ContentType => 'text/plain', Status => 'new' } ],
+ 'Authorization' => $auth,
+ );
+ is( $res->code, 201, 'status code' );
+ is_deeply(
+ $mech->json_response,
+ [ [ $ticket_ids[0], "Correspondence added", "Status changed from 'open' to 'new'" ] ],
+ 'Correspondence added'
+ );
+ $user->PrincipalObj->RevokeRight( Right => $_ ) for qw/ReplyToTicket ModifyTicket/;
+}
+
+{
+ diag "no CommentOnTicket right";
+ my $res = $mech->post_json(
+ "$rest_base_path/tickets/bulk/comment",
+ [ { id => $ticket_ids[0], Content => 'test comment', ContentType => 'text/plain' } ],
+ 'Authorization' => $auth,
+ );
+ is( $res->code, 201, "status code" );
+ is_deeply( $mech->json_response, [ [ $ticket_ids[0], "Permission Denied", ] ], 'permission denied' );
+
+ diag "grant CommentOnTicket right";
+ $user->PrincipalObj->GrantRight( Right => 'CommentOnTicket' );
+
+ $res = $mech->post_json(
+ "$rest_base_path/tickets/bulk/comment",
+ [ { id => $ticket_ids[0], Content => 'test comment', ContentType => 'text/plain' } ],
+ 'Authorization' => $auth,
+ );
+ is( $res->code, 201, 'status code' );
+ is_deeply( $mech->json_response, [ [ $ticket_ids[0], "Comments added", ] ], 'Comments added' );
+
+ # Do status change along with comment
+ $user->PrincipalObj->GrantRight( Right => $_ ) for qw/ShowTicket ModifyTicket/;
+ $res = $mech->post_json(
+ "$rest_base_path/tickets/bulk/comment",
+ [ { id => $ticket_ids[0], Content => 'test comment', ContentType => 'text/plain', Status => 'open' } ],
+ 'Authorization' => $auth,
+ );
+ is( $res->code, 201, 'status code' );
+ is_deeply(
+ $mech->json_response,
+ [ [ $ticket_ids[0], "Comments added", "Status changed from 'new' to 'open'" ] ],
+ 'Comments added'
+ );
+ $user->PrincipalObj->RevokeRight( Right => $_ ) for qw/CommentOnTicket ModifyTicket/;
+}
+
done_testing;
commit 6e105df77e4d5a182c2e24d64c5bc3430df7e2a4
Author: sunnavy <sunnavy at bestpractical.com>
Date: Fri Jun 18 03:22:37 2021 +0800
Document /tickets/bulk/correspond and /tickets/bulk/comment endpoints
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index fdf5d66..f98f786 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -492,6 +492,10 @@ curl for SSL like --cacert.
PUT /tickets/bulk
update multiple tickets' metadata; provide JSON content(array of hashes)
+ POST /tickets/bulk/correspond
+ POST /tickets/bulk/comment
+ add a reply or comment to multiple tickets; provide JSON content(array of hashes)
+
=head3 Ticket Examples
Below are some examples using the endpoints above.
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list