[Bps-public-commit] rt-extension-rest2 branch, transaction-cfs, created. 1.07-1-ga0afa67

Michel Rodriguez michel at bestpractical.com
Thu Aug 8 17:11:11 EDT 2019


The branch, transaction-cfs has been created
        at  a0afa6790079e9bb6396cb8d9c0b2e9177c35935 (commit)

- Log -----------------------------------------------------------------
commit a0afa6790079e9bb6396cb8d9c0b2e9177c35935
Author: michel <michel at bestpractical.com>
Date:   Tue Jul 30 23:48:47 2019 +0200

    Added Transaction Custom Field updates on Correspond and Comment

diff --git a/README b/README
index 40138e3..cbab6db 100644
--- a/README
+++ b/README
@@ -275,6 +275,13 @@ USAGE
             -d '{ "Content": "Testing a correspondence", "ContentType": "text/plain" }'
             'https://myrt.com/REST/2.0/ticket/6/correspond'
 
+        # Correspond a ticket with a transaction custom field
+        curl -X POST -H "Content-Type: application/json" -u 'root:password'
+            -d '{ "Content": "Testing a correspondence", "ContentType": "text/plain",
+                  "TxnCustomFields": {"MyField": "custom field value"}
+                }'
+            'https://myrt.com/REST/2.0/ticket/6/correspond'
+
         # Comment a ticket
         curl -X POST -H "Content-Type: text/plain" -u 'root:password'
             -d 'Testing a comment'
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index c4b61c6..195d883 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -311,6 +311,13 @@ Below are some examples using the endpoints above.
         -d '{ "Content": "Testing a correspondence", "ContentType": "text/plain" }'
         'https://myrt.com/REST/2.0/ticket/6/correspond'
 
+    # Correspond a ticket with a transaction custom field
+    curl -X POST -H "Content-Type: application/json" -u 'root:password'
+        -d '{ "Content": "Testing a correspondence", "ContentType": "text/plain",
+              "TxnCustomFields": {"MyField": "custom field value"}
+            }'
+        'https://myrt.com/REST/2.0/ticket/6/correspond'
+
     # Comment a ticket
     curl -X POST -H "Content-Type: text/plain" -u 'root:password'
         -d 'Testing a comment'
diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index 57c8ef1..62cf377 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -69,6 +69,15 @@ sub add_message {
         Subject   => $args{Subject},
     );
 
+    # we use $txn_ret and $txn_msg so we don't interfere with the main $ret and $msg
+    # see also comment below on the call to UpdateCustomFields
+    my ( $txn_ret, $txn_msg, $TxnCustomFields ) = $self->_massage_txn_custom_fields(
+        $args{TxnCustomFields} || $args{TransactionCustomFields} );
+
+    if ( ! $txn_ret ) {
+        return error_as_json( $self->response, \400, $txn_msg );
+    }
+
     my ( $Trans, $msg, $TransObj ) ;
 
     if ($self->type eq 'correspond') {
@@ -93,12 +102,85 @@ sub add_message {
             \400, $msg || "Message failed for unknown reason");
     }
 
+    my ( $update_ret, $update_msg ) = $self->_update_txn_custom_fields( $TransObj, $TxnCustomFields );
+    $msg .= " - Error: transaction custom fields not updated" unless $update_ret;
+
     $self->created_transaction($TransObj);
     $self->response->body(JSON::to_json([$msg], { pretty => 1 }));
 
     return 1;
 }
 
+# takes a TxnCustomFields (or TransactionCustomFields) argument in
+# returns 
+#     a status code, 
+#    a message (in case of error)
+#     a hashref where the CF names are translated into the full
+#        form Object-RT::Transaction--CustomField-<id> that can
+#        be used by UpdateCustomFields
+#        the value is undef if no argument was passed
+sub _massage_txn_custom_fields {
+    my $self = shift;
+    my $txn_cf_by_name = shift;
+
+    # if there are no transaction custom fields we're good
+    if( ! $txn_cf_by_name ) {
+        return ( 1, '', undef);
+    }
+
+    my $user = $self->request->env->{"rt.current_user"};
+
+    # we need the queue to get the transaction custom fields that can apply to it
+    my $queue_obj = $self->record->QueueObj;
+
+    # Pre-check that the user can actually update the CFs
+    unless ( $queue_obj->CurrentUserHasRight('ModifyCustomField') ) {
+        RT->Logger->error( "Permission denied: " . $user->Name . " cannot modify transaction custom fields");
+        return ( 0, "Permission denied", undef );
+    }
+
+    # generate a hash suitable for UpdateCustomFields
+    # ie the keys are the "full names" of the custom fields 
+    my %txn_custom_fields;
+    foreach my $cf_name ( %{$txn_cf_by_name} ) {
+        my $cf_obj = $queue_obj->LoadCustomFieldByIdentifier($cf_name);
+
+        unless ( $cf_obj and $cf_obj->Id ) {
+            RT->Logger->error( "Unable to load transaction custom field: $cf_name" );
+            return ( 0, "Unable to load transaction custom field: $cf_name", undef );
+        }
+
+        my $txn_input_name = RT::Interface::Web::GetCustomFieldInputNamePrefix(
+                             Object      => $queue_obj,
+                             CustomField => $cf_obj,
+        );
+
+        $txn_custom_fields{$txn_input_name} = $txn_cf_by_name->{$cf_name};
+    }
+
+    return ( 1, "Custom fields validated", \%txn_custom_fields );
+}
+
+sub _update_txn_custom_fields {
+    my $self = shift;
+    my $TransObj = shift;
+    my $TxnCustomFields = shift;
+
+    my ( $txn_ret, $txn_msg );
+    if ( keys %$TxnCustomFields ) {
+        ( $txn_ret, $txn_msg ) = $TransObj->UpdateCustomFields( %$TxnCustomFields );
+
+        if ( !$txn_ret ) {
+            # the correspond/comment is already a success, the mails have been sent
+            # so we can't return an error here
+            RT->Logger->error( "Could not update transaction custom fields: $txn_msg" );
+            return ( 0, $txn_msg );
+        }
+    }
+
+    return ( 1, $txn_msg );
+}
+
 sub create_path {
     my $self = shift;
     my $id = $self->created_transaction->Id;
diff --git a/xt/transaction-customfields.t b/xt/transaction-customfields.t
new file mode 100644
index 0000000..7d3aee8
--- /dev/null
+++ b/xt/transaction-customfields.t
@@ -0,0 +1,113 @@
+
+use strict;
+use warnings;
+use RT::Extension::REST2::Test tests => undef;
+use Test::Deep;
+
+my $mech = RT::Extension::REST2::Test->mech;
+
+my $auth = RT::Extension::REST2::Test->authorization_header;
+my $rest_base_path = '/REST/2.0';
+my $admin = RT::Extension::REST2::Test->user;
+$admin->PrincipalObj->GrantRight( Right => 'SuperUser' );
+
+my $queue = RT::Test->load_or_create_queue( Name => "General" );
+
+my( $id, $msg);
+
+my $cf = RT::CustomField->new( RT->SystemUser );
+my $cfid;
+($cfid, $msg) = $cf->Create(Name => 'TxnCF', Type => 'Freeform', MaxValues => '0', LookupType => RT::Transaction->CustomFieldLookupType );
+ok($cfid,$msg);
+
+
+($id,$msg) = $cf->AddToObject($queue);
+ok($id,$msg);
+
+my $ticket = RT::Ticket->new(RT->SystemUser);
+my $transid;
+($id,$transid, $msg) = $ticket->Create(Queue => $queue->id, Subject => 'TxnCF test',);
+ok($id,$msg);
+
+my $res = $mech->get("$rest_base_path/ticket/$id", 'Authorization' => $auth);
+    is( $res->code, 200);
+
+my $tnb=0;
+{   $tnb++;
+    my $payload = { Content         => "reply $tnb",
+                    ContentType     => "text/plain",
+                    TxnCustomFields => { "TxnCF" => "txncf value $tnb"},
+                  };
+    my $res = $mech->post_json("$rest_base_path/ticket/$id/correspond", $payload, 'Authorization' => $auth);
+    is( $res->code, 201, 'correspond response code is 201');
+    is_deeply( $mech->json_response, [ "Correspondence added" ], 'message is "Correspondence Added"');
+
+    my $txn= last_txn( $id, 'Correspond');
+    is( $txn->{CustomFields}->{$cfid}->[0], "txncf value 1", 'CustomField by id');
+}
+
+{
+    my $payload = { Content         => "reply #2",
+                    ContentType     => "text/plain",
+                    TxnCustomFields => { "not a real CF name" => "txncf value"},
+                  };
+    my $res = $mech->post_json("$rest_base_path/ticket/$id/correspond", $payload, 'Authorization' => $auth);
+    is( $res->code, 400, 'correspond response code is 400');
+    is( $mech->json_response->{message}, "unknown transaction custom field: not a real CF name", 'wrong cf name');
+}
+
+done_testing(); exit;
+
+# this is clumsy and brittle, and should probably be replaced by direct access to the data through RT::Test
+
+sub last_txn {
+    my( $ticket_id, $type ) = @_;
+    $type ||= 'Correspond';
+    my $ticket_res  = $mech->get("$rest_base_path/ticket/$id", 'Authorization' => $auth);
+
+    # build the list of txn
+    my $history_url = link_of_type( $mech->json_response, 'history' );
+    $mech->get( $history_url, 'Authorization' => $auth);
+    my $content= $mech->json_response;
+    my @items= @{$content->{items}};
+    if( $content->{total} >  $content->{per_page}) {
+        my $last_page = int( $content->{total} / $content->{per_page} ) + 1;
+        foreach my $page (2..$last_page) {
+            $mech->get( "$history_url?page=$page", 'Authorization' => $auth);
+            $content= $mech->json_response;
+            push @items, @{$content->{items}};
+        }
+    }
+
+    # get the txn record for the last action of type $type
+    my $last_txn;
+    foreach my $txn (reverse @items) {
+        $mech->get( $txn->{_url}, 'Authorization' => $auth );
+        $content= $mech->json_response;
+        #use DDP; warn "txn content:\n"; p $content;
+        if( $content->{Type} eq $type ) {
+            $last_txn = $content;
+            last;
+        }
+    }
+
+    if( $last_txn) {
+        return $last_txn;
+    }
+    else {
+        return;
+    }
+}
+
+sub link_of_type {
+    my( $json, $ref)= @_;
+    my $links = $json->{_hyperlinks};
+    foreach my $link (@$links) {
+        if( $link->{ref} eq $ref) {
+            return $link->{_url};
+        }
+    }
+    return;
+}
+
+

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list