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

Jim Brandt jbrandt at bestpractical.com
Fri Aug 16 17:20:54 EDT 2019


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

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

    Add 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..1c3ef33 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -69,8 +69,7 @@ sub add_message {
         Subject   => $args{Subject},
     );
 
-    my ( $Trans, $msg, $TransObj ) ;
-
+    my ( $Trans, $msg, $TransObj );
     if ($self->type eq 'correspond') {
         ( $Trans, $msg, $TransObj ) = $self->record->Correspond(
             MIMEObj   => $MIME,
@@ -93,12 +92,62 @@ sub add_message {
             \400, $msg || "Message failed for unknown reason");
     }
 
+    my ( $update_ret, $update_msg ) = $self->_update_txn_custom_fields(
+        $TransObj, $args{TxnCustomFields} || $args{TransactionCustomFields} );
+    $msg .= " - CF Processing Error: transaction custom fields not updated" unless $update_ret;
+
     $self->created_transaction($TransObj);
     $self->response->body(JSON::to_json([$msg], { pretty => 1 }));
 
     return 1;
 }
 
+sub _update_txn_custom_fields {
+    my $self = shift;
+    my $TransObj = shift;
+    my $TxnCustomFields = shift;
+
+    # generate a hash suitable for UpdateCustomFields
+    # ie the keys are the "full names" of the custom fields
+    my %txn_custom_fields;
+
+    # Create an empty Transaction object to pass to GetCustomFieldInputName
+    # UpdateCustomFields expects ARGS where the Txn input name doesn't have
+    # an Id yet. It uses $self to determine which Txn to operate on.
+    my $EmptyTxn = RT::Transaction->new( RT->SystemUser );
+
+    foreach my $cf_name ( keys %{$TxnCustomFields} ) {
+        my $cf_obj = $TransObj->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::GetCustomFieldInputName(
+                             Object      => $EmptyTxn,
+                             CustomField => $cf_obj,
+                             Grouping    => undef
+        );
+
+        $txn_custom_fields{$txn_input_name} = $TxnCustomFields->{$cf_name};
+    }
+
+    my ( $txn_ret, $txn_msg );
+    if ( keys %$TxnCustomFields ) {
+        ( $txn_ret, $txn_msg ) = $TransObj->UpdateCustomFields( %txn_custom_fields );
+
+        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..53a91b6
--- /dev/null
+++ b/xt/transaction-customfields.t
@@ -0,0 +1,74 @@
+use strict;
+use warnings;
+use RT::Extension::REST2::Test tests => undef;
+use Test::Deep;
+
+my $mech = RT::Extension::REST2::Test->mech;
+my ( $baseurl, $m ) = RT::Test->started_ok;
+diag "Started server at $baseurl";
+
+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 => 'FreeformSingle', 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 ( $ticket1_id, $transid );
+($ticket1_id, $transid, $msg) = $ticket->Create(Queue => $queue->id, Subject => 'TxnCF test',);
+ok( $ticket1_id, $msg );
+
+my $res = $mech->get("$rest_base_path/ticket/$ticket1_id", 'Authorization' => $auth);
+is( $res->code, 200, 'Fetched ticket via REST2 API');
+
+{
+    my $payload = { Content         => "reply one",
+                    ContentType     => "text/plain",
+                    TxnCustomFields => { "TxnCF" => "txncf value one"},
+                  };
+    my $res = $mech->post_json("$rest_base_path/ticket/$ticket1_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 $ticket = RT::Ticket->new(RT->SystemUser);
+    my ( $ret, $msg ) = $ticket->Load( $ticket1_id );
+    ok( $ret, $msg );
+    my $txns = $ticket->Transactions;
+    $txns->Limit( FIELD => 'Type', VALUE => 'Correspond' );
+    my $txn = $txns->Last;
+    ok( $txn->Id, "Found Correspond transaction" );
+    is( $txn->FirstCustomFieldValue('TxnCF'), "txncf value one", 'Found transaction custom field');
+}
+
+# TODO Determine how to use RT::Test::Web tools to check and clear expected warnings
+
+=pod
+
+{
+    my $payload = { Content         => "reply two",
+                    ContentType     => "text/plain",
+                    TxnCustomFields => { "not a real CF name" => "txncf value"},
+                  };
+    my $res = $mech->post_json("$rest_base_path/ticket/$ticket1_id/correspond", $payload, 'Authorization' => $auth);
+
+    # Doesn't work like RT
+    my @warnings = $m->get_warnings;
+
+    is( $res->code, 201, 'Correspond response code is 201 because correspond succeeded');
+    is( $mech->json_response, [ "Correspondence added - CF Processing Error: transaction custom fields not updated" ], 'Bogus cf name');
+}
+
+=cut
+
+done_testing();

commit 3c29c50242146ee6f664262ac5ae31c2a608290d
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Aug 9 11:31:05 2019 -0400

    Move custom field updater to Utils for use elsewhere

diff --git a/lib/RT/Extension/REST2/Resource/Record/Writable.pm b/lib/RT/Extension/REST2/Resource/Record/Writable.pm
index 6d04d3b..4036425 100644
--- a/lib/RT/Extension/REST2/Resource/Record/Writable.pm
+++ b/lib/RT/Extension/REST2/Resource/Record/Writable.pm
@@ -5,7 +5,7 @@ use warnings;
 use Moose::Role;
 use namespace::autoclean;
 use JSON ();
-use RT::Extension::REST2::Util qw( deserialize_record error_as_json expand_uid );
+use RT::Extension::REST2::Util qw( deserialize_record error_as_json expand_uid update_custom_fields );
 use List::MoreUtils 'uniq';
 
 with 'RT::Extension::REST2::Resource::Role::RequestBodyIsJSON'
@@ -52,7 +52,7 @@ sub update_record {
         AttributesRef => [ $self->record->WritableAttributes ],
     );
 
-    push @results, $self->_update_custom_fields($data->{CustomFields});
+    push @results, update_custom_fields($self->record, $data->{CustomFields});
     push @results, $self->_update_role_members($data);
     push @results, $self->_update_disabled($data->{Disabled})
       unless grep { $_ eq 'Disabled' } $self->record->WritableAttributes;
@@ -62,84 +62,6 @@ sub update_record {
     return @results;
 }
 
-sub _update_custom_fields {
-    my $self = shift;
-    my $data = shift;
-
-    my $record = $self->record;
-    my @results;
-
-    foreach my $cfid (keys %{ $data }) {
-        my $val = $data->{$cfid};
-
-        my $cf = $record->LoadCustomFieldByIdentifier($cfid);
-        next unless $cf->ObjectTypeFromLookupType($cf->__Value('LookupType'))->isa(ref $record);
-
-        if ($cf->SingleValue) {
-            if (ref($val) eq 'ARRAY') {
-                $val = $val->[0];
-            }
-            elsif (ref($val)) {
-                die "Invalid value type for CustomField $cfid";
-            }
-
-            my ($ok, $msg) = $record->AddCustomFieldValue(
-                Field => $cf,
-                Value => $val,
-            );
-            push @results, $msg;
-        }
-        else {
-            my %count;
-            my @vals = ref($val) eq 'ARRAY' ? @$val : $val;
-            for (@vals) {
-                $count{$_}++;
-            }
-
-            my $ocfvs = $cf->ValuesForObject( $record );
-            my %ocfv_id;
-            while (my $ocfv = $ocfvs->Next) {
-                my $content = $ocfv->Content;
-                $count{$content}--;
-                push @{ $ocfv_id{$content} }, $ocfv->Id;
-            }
-
-            # we want to provide a stable order, so first go by the order
-            # provided in the argument list, and then for any custom fields
-            # that are being removed, remove in sorted order
-            for my $key (uniq(@vals, sort keys %count)) {
-                my $count = $count{$key};
-                if ($count == 0) {
-                    # new == old, no change needed
-                }
-                elsif ($count > 0) {
-                    # new > old, need to add new
-                    while ($count-- > 0) {
-                        my ($ok, $msg) = $record->AddCustomFieldValue(
-                            Field => $cf,
-                            Value => $key,
-                        );
-                        push @results, $msg;
-                    }
-                }
-                elsif ($count < 0) {
-                    # old > new, need to remove old
-                    while ($count++ < 0) {
-                        my $id = shift @{ $ocfv_id{$key} };
-                        my ($ok, $msg) = $record->DeleteCustomFieldValue(
-                            Field   => $cf,
-                            ValueId => $id,
-                        );
-                        push @results, $msg;
-                    }
-                }
-            }
-        }
-    }
-
-    return @results;
-}
-
 sub _update_role_members {
     my $self = shift;
     my $data = shift;
@@ -306,7 +228,7 @@ sub create_record {
     my ($ok, @rest) = $record->$method(%args);
 
     if ($ok && $cfs) {
-        $self->_update_custom_fields($cfs);
+        update_custom_fields($record, $cfs);
     }
 
     return ($ok, @rest);
diff --git a/lib/RT/Extension/REST2/Util.pm b/lib/RT/Extension/REST2/Util.pm
index e814c1c..e59a69c 100644
--- a/lib/RT/Extension/REST2/Util.pm
+++ b/lib/RT/Extension/REST2/Util.pm
@@ -4,6 +4,7 @@ use warnings;
 
 use JSON ();
 use Scalar::Util qw( blessed );
+use List::MoreUtils 'uniq';
 
 use Sub::Exporter -setup => {
     exports => [qw[
@@ -19,6 +20,7 @@ use Sub::Exporter -setup => {
         query_string
         custom_fields_for
         format_datetime
+        update_custom_fields
     ]]
 };
 
@@ -247,4 +249,82 @@ sub custom_fields_for {
     return;
 }
 
+sub update_custom_fields {
+    my $record = shift;
+    my $data = shift;
+
+    my @results;
+
+    foreach my $cfid (keys %{ $data }) {
+        my $val = $data->{$cfid};
+
+        my $cf = $record->LoadCustomFieldByIdentifier($cfid);
+        next unless $cf->ObjectTypeFromLookupType($cf->__Value('LookupType'))->isa(ref $record);
+
+        if ($cf->SingleValue) {
+            if (ref($val) eq 'ARRAY') {
+                $val = $val->[0];
+            }
+            elsif (ref($val)) {
+                die "Invalid value type for CustomField $cfid";
+            }
+
+            my ($ok, $msg) = $record->AddCustomFieldValue(
+                Field => $cf,
+                Value => $val,
+            );
+            push @results, $msg;
+        }
+        else {
+            my %count;
+            my @vals = ref($val) eq 'ARRAY' ? @$val : $val;
+            for (@vals) {
+                $count{$_}++;
+            }
+
+            my $ocfvs = $cf->ValuesForObject( $record );
+            my %ocfv_id;
+            while (my $ocfv = $ocfvs->Next) {
+                my $content = $ocfv->Content;
+                $count{$content}--;
+                push @{ $ocfv_id{$content} }, $ocfv->Id;
+            }
+
+            # we want to provide a stable order, so first go by the order
+            # provided in the argument list, and then for any custom fields
+            # that are being removed, remove in sorted order
+            for my $key (uniq(@vals, sort keys %count)) {
+                my $count = $count{$key};
+                if ($count == 0) {
+                    # new == old, no change needed
+                }
+                elsif ($count > 0) {
+                    # new > old, need to add new
+                    while ($count-- > 0) {
+                        my ($ok, $msg) = $record->AddCustomFieldValue(
+                            Field => $cf,
+                            Value => $key,
+                        );
+                        push @results, $msg;
+                    }
+                }
+                elsif ($count < 0) {
+                    # old > new, need to remove old
+                    while ($count++ < 0) {
+                        my $id = shift @{ $ocfv_id{$key} };
+                        my ($ok, $msg) = $record->DeleteCustomFieldValue(
+                            Field   => $cf,
+                            ValueId => $id,
+                        );
+                        push @results, $msg;
+                    }
+                }
+            }
+        }
+    }
+
+    return @results;
+}
+
+
 1;

commit 52ef66d789ed1ddbd925464d63a954f2ef72cc87
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Aug 9 13:39:24 2019 -0400

    Add custom fields on comment and correspond
    
    Also refactor return values for transaction cf updates
    to return messages the same as ticket custom field
    updates.

diff --git a/README b/README
index cbab6db..5871ce5 100644
--- a/README
+++ b/README
@@ -278,15 +278,19 @@ USAGE
         # 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"}
-                }'
+                  "TxnCustomFields": {"MyField": "custom field value"} }'
             'https://myrt.com/REST/2.0/ticket/6/correspond'
 
-        # Comment a ticket
+        # Comment on a ticket
         curl -X POST -H "Content-Type: text/plain" -u 'root:password'
             -d 'Testing a comment'
             'https://myrt.com/REST/2.0/ticket/6/comment'
 
+        # Comment on a ticket with custom field update
+        curl -X POST -H "Content-Type: text/plain" -u 'root:password'
+            -d '{ "Content": "Testing a comment", "ContentType": "text/plain", "CustomFields": {"Severity": "High"} }'
+            'https://myrt.com/REST/2.0/ticket/6/comment'
+
         # Create an Asset
         curl -X POST -H "Content-Type: application/json" -u 'root:password'
             -d '{"Name" : "Asset From Rest", "Catalog" : "General assets", "Content" : "Some content"}'
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 195d883..e0315c5 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -314,15 +314,19 @@ Below are some examples using the endpoints above.
     # 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"}
-            }'
+              "TxnCustomFields": {"MyField": "custom field value"} }'
         'https://myrt.com/REST/2.0/ticket/6/correspond'
 
-    # Comment a ticket
+    # Comment on a ticket
     curl -X POST -H "Content-Type: text/plain" -u 'root:password'
         -d 'Testing a comment'
         'https://myrt.com/REST/2.0/ticket/6/comment'
 
+    # Comment on a ticket with custom field update
+    curl -X POST -H "Content-Type: text/plain" -u 'root:password'
+        -d '{ "Content": "Testing a comment", "ContentType": "text/plain", "CustomFields": {"Severity": "High"} }'
+        'https://myrt.com/REST/2.0/ticket/6/comment'
+
     # Create an Asset
     curl -X POST -H "Content-Type: application/json" -u 'root:password'
         -d '{"Name" : "Asset From Rest", "Catalog" : "General assets", "Content" : "Some content"}'
diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index 1c3ef33..a35cd51 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -6,7 +6,7 @@ use Moose;
 use namespace::autoclean;
 
 extends 'RT::Extension::REST2::Resource';
-use RT::Extension::REST2::Util qw( error_as_json );
+use RT::Extension::REST2::Util qw( error_as_json update_custom_fields );
 
 sub dispatch_rules {
     Path::Dispatcher::Rule::Regex->new(
@@ -61,6 +61,7 @@ sub from_json {
 sub add_message {
     my $self = shift;
     my %args = @_;
+    my @results;
 
     my $MIME = HTML::Mason::Commands::MakeMIMEEntity(
         Interface => 'REST',
@@ -92,12 +93,12 @@ sub add_message {
             \400, $msg || "Message failed for unknown reason");
     }
 
-    my ( $update_ret, $update_msg ) = $self->_update_txn_custom_fields(
-        $TransObj, $args{TxnCustomFields} || $args{TransactionCustomFields} );
-    $msg .= " - CF Processing Error: transaction custom fields not updated" unless $update_ret;
+    push @results, $msg;
+    push @results, update_custom_fields($self->record, $args{CustomFields});
+    push @results, $self->_update_txn_custom_fields( $TransObj, $args{TxnCustomFields} || $args{TransactionCustomFields} );
 
     $self->created_transaction($TransObj);
-    $self->response->body(JSON::to_json([$msg], { pretty => 1 }));
+    $self->response->body(JSON::to_json(\@results, { pretty => 1 }));
 
     return 1;
 }
@@ -106,6 +107,7 @@ sub _update_txn_custom_fields {
     my $self = shift;
     my $TransObj = shift;
     my $TxnCustomFields = shift;
+    my @results;
 
     # generate a hash suitable for UpdateCustomFields
     # ie the keys are the "full names" of the custom fields
@@ -121,7 +123,7 @@ sub _update_txn_custom_fields {
 
         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 );
+            push @results, "Unable to load transaction custom field: $cf_name";
         }
 
         my $txn_input_name = RT::Interface::Web::GetCustomFieldInputName(
@@ -133,19 +135,18 @@ sub _update_txn_custom_fields {
         $txn_custom_fields{$txn_input_name} = $TxnCustomFields->{$cf_name};
     }
 
-    my ( $txn_ret, $txn_msg );
     if ( keys %$TxnCustomFields ) {
-        ( $txn_ret, $txn_msg ) = $TransObj->UpdateCustomFields( %txn_custom_fields );
+        # UpdateCustomFields currently doesn't return messages on updates
+        # Stub it out for now.
+        my @return = $TransObj->UpdateCustomFields( %txn_custom_fields );
 
-        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 );
+        # Simulate return messages until we get real results
+        if ( @return && $return[0] == 1 ) {
+            push @results, 'Custom fields updated';
         }
     }
 
-    return ( 1, $txn_msg );
+    return @results;
 }
 
 sub create_path {
diff --git a/xt/ticket-customfields.t b/xt/ticket-customfields.t
index 5e8174f..d276ff3 100644
--- a/xt/ticket-customfields.t
+++ b/xt/ticket-customfields.t
@@ -260,6 +260,37 @@ my ($ticket_url, $ticket_id);
     is_deeply($content->{CustomFields}, { $single_cf_id => ['Modified Again'], $multi_cf_id => [] }, 'Same CF value');
 }
 
+# Ticket Comment with custom field
+{
+    my $payload = {
+        Content     => 'This is some content for a comment',
+        ContentType => 'text/plain',
+        Subject     => 'This is a subject',
+        CustomFields => {
+            $single_cf_id => 'Yet another modified CF',
+        },
+    };
+
+    $user->PrincipalObj->GrantRight( Right => 'CommentOnTicket' );
+
+    my $res = $mech->get($ticket_url,
+        'Authorization' => $auth,
+    );
+    is($res->code, 200);
+    my $content = $mech->json_response;
+
+    my ($hypermedia) = grep { $_->{ref} eq 'comment' } @{ $content->{_hyperlinks} };
+    ok($hypermedia, 'got comment hypermedia');
+    like($hypermedia->{_url}, qr[$rest_base_path/ticket/$ticket_id/comment$]);
+
+    $res = $mech->post_json($mech->url_for_hypermedia('comment'),
+        $payload,
+        'Authorization' => $auth,
+    );
+    is($res->code, 201);
+    cmp_deeply($mech->json_response, [re(qr/Comments added|Message recorded/), "Single Modified Again changed to Yet another modified CF"]);
+}
+
 # Ticket Creation with ModifyCustomField
 {
     my $payload = {
diff --git a/xt/transaction-customfields.t b/xt/transaction-customfields.t
index 53a91b6..93c5650 100644
--- a/xt/transaction-customfields.t
+++ b/xt/transaction-customfields.t
@@ -39,7 +39,7 @@ is( $res->code, 200, 'Fetched ticket via REST2 API');
                   };
     my $res = $mech->post_json("$rest_base_path/ticket/$ticket1_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"');
+    is_deeply( $mech->json_response, [ "Correspondence added", "Custom fields updated" ], 'message is "Correspondence Added"');
 
     my $ticket = RT::Ticket->new(RT->SystemUser);
     my ( $ret, $msg ) = $ticket->Load( $ticket1_id );

commit 4e81bdafa96d090858eba0e5f2a15f043759c302
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Aug 10 02:57:46 2019 +0800

    No need to pass EmptyTxn to get custom field input names
    
    Passing an empty object is equivalent to not passing it at all.

diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index a35cd51..e7b18dd 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -113,11 +113,6 @@ sub _update_txn_custom_fields {
     # ie the keys are the "full names" of the custom fields
     my %txn_custom_fields;
 
-    # Create an empty Transaction object to pass to GetCustomFieldInputName
-    # UpdateCustomFields expects ARGS where the Txn input name doesn't have
-    # an Id yet. It uses $self to determine which Txn to operate on.
-    my $EmptyTxn = RT::Transaction->new( RT->SystemUser );
-
     foreach my $cf_name ( keys %{$TxnCustomFields} ) {
         my $cf_obj = $TransObj->LoadCustomFieldByIdentifier($cf_name);
 
@@ -127,7 +122,6 @@ sub _update_txn_custom_fields {
         }
 
         my $txn_input_name = RT::Interface::Web::GetCustomFieldInputName(
-                             Object      => $EmptyTxn,
                              CustomField => $cf_obj,
                              Grouping    => undef
         );

commit cf8a9eb55e7a0d5b784b933b48852c6afa448ef0
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Aug 10 03:10:23 2019 +0800

    Always call txn->UpdateCustomFields for txn cfs with default values
    
    As "AddCustomFieldDefaultValues" is called inside UpdateCustomFields.

diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index e7b18dd..dd966ca 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -129,11 +129,11 @@ sub _update_txn_custom_fields {
         $txn_custom_fields{$txn_input_name} = $TxnCustomFields->{$cf_name};
     }
 
-    if ( keys %$TxnCustomFields ) {
-        # UpdateCustomFields currently doesn't return messages on updates
-        # Stub it out for now.
-        my @return = $TransObj->UpdateCustomFields( %txn_custom_fields );
+    # UpdateCustomFields currently doesn't return messages on updates
+    # Stub it out for now.
+    my @return = $TransObj->UpdateCustomFields( %txn_custom_fields );
 
+    if ( keys %txn_custom_fields ) {
         # Simulate return messages until we get real results
         if ( @return && $return[0] == 1 ) {
             push @results, 'Custom fields updated';

commit eb6298600a231888c5a8398480019771ebe4a441
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Aug 9 16:42:28 2019 -0400

    Prep 1.08 release

diff --git a/Changes b/Changes
index c756d8c..3fb4d27 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,9 @@
 Revision history for RT-Extension-REST2
 
+1.08 2019-08-09
+ - Accept transaction custom fields on comment/correspond
+ - Accept ticket custom fields on comment/correspond
+
 1.07 2019-05-24
  - Accept 'Content' as a parameter on create. The documentation previously showed
    this in examples, but it wasn't yet supported. Now it works as documented.
diff --git a/MANIFEST b/MANIFEST
index 7ef88eb..3b90cd6 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -82,6 +82,7 @@ xt/ticket-links.t
 xt/ticket-watchers.t
 xt/tickets-bulk.t
 xt/tickets.t
+xt/transaction-customfields.t
 xt/transactions.t
 xt/user-customfields.t
 xt/user-memberships.t
diff --git a/META.yml b/META.yml
index 65f84bc..c870959 100644
--- a/META.yml
+++ b/META.yml
@@ -43,6 +43,6 @@ requires:
   perl: 5.10.1
 resources:
   license: http://opensource.org/licenses/gpl-license.php
-version: '1.07'
+version: '1.08'
 x_module_install_rtx_version: '0.40'
 x_requires_rt: 4.2.4
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index e0315c5..64bdc9b 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -4,7 +4,7 @@ use 5.010001;
 
 package RT::Extension::REST2;
 
-our $VERSION = '1.07';
+our $VERSION = '1.08';
 our $REST_PATH = '/REST/2.0';
 
 use Plack::Builder;

commit 94a5403e21ebc4217f86e3be9b2c49cd2f4777ea
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Aug 16 09:24:13 2019 -0400

    Add custom field names in hyperlink data

diff --git a/lib/RT/Extension/REST2/Resource/Record/Hypermedia.pm b/lib/RT/Extension/REST2/Resource/Record/Hypermedia.pm
index 93e6d85..2f27431 100644
--- a/lib/RT/Extension/REST2/Resource/Record/Hypermedia.pm
+++ b/lib/RT/Extension/REST2/Resource/Record/Hypermedia.pm
@@ -91,6 +91,7 @@ sub _customfield_links {
             push @links, {
                 %$entry,
                 ref => 'customfield',
+                name => $cf->Name,
             };
         }
     }
diff --git a/xt/asset-customfields.t b/xt/asset-customfields.t
index 499c400..d16bb73 100644
--- a/xt/asset-customfields.t
+++ b/xt/asset-customfields.t
@@ -119,11 +119,13 @@ my ($asset_url, $asset_id);
         [{
             ref => 'customfield',
             id  => $single_cf_id,
+            name => 'Single',
             type => 'customfield',
             _url => re(qr[$rest_base_path/customfield/$single_cf_id$]),
         }, {
             ref => 'customfield',
             id  => $multi_cf_id,
+            name => 'Multi',
             type => 'customfield',
             _url => re(qr[$rest_base_path/customfield/$multi_cf_id$]),
         }],
diff --git a/xt/queues.t b/xt/queues.t
index a8396b2..1a4ec4e 100644
--- a/xt/queues.t
+++ b/xt/queues.t
@@ -9,6 +9,17 @@ my $user = RT::Extension::REST2::Test->user;
 
 $user->PrincipalObj->GrantRight( Right => 'SuperUser' );
 
+my $queue_obj = RT::Test->load_or_create_queue( Name => "General" );
+
+my $single_cf = RT::CustomField->new( RT->SystemUser );
+
+my ($ok, $msg) = $single_cf->Create( Name => 'Single', Type => 'FreeformSingle', LookupType => RT::Queue->CustomFieldLookupType );
+ok($ok, $msg);
+
+($ok, $msg) = $single_cf->AddToObject($queue_obj);
+ok($ok, $msg);
+my $single_cf_id = $single_cf->Id;
+
 my $queue_url;
 # search Name = General
 {
@@ -51,19 +62,26 @@ my $queue_url;
     ok(exists $content->{$_}, "got $_") for @fields;
 
     my $links = $content->{_hyperlinks};
-    is(scalar @$links, 3);
+    is(scalar @$links, 4);
 
     is($links->[0]{ref}, 'self');
     is($links->[0]{id}, 1);
     is($links->[0]{type}, 'queue');
     like($links->[0]{_url}, qr[$rest_base_path/queue/1$]);
 
-    is($links->[1]{ref}, 'history');
-    like($links->[1]{_url}, qr[$rest_base_path/queue/1/history$]);
+    is($links->[1]{ref}, 'customfield');
+    like($links->[1]{_url}, qr[$rest_base_path/customfield/$single_cf_id$]);
+    is($links->[1]{name}, 'Single');
 
-    is($links->[2]{ref}, 'create');
-    is($links->[2]{type}, 'ticket');
-    like($links->[2]{_url}, qr[$rest_base_path/ticket\?Queue=1$]);
+    is($links->[2]{ref}, 'history');
+    like($links->[2]{_url}, qr[$rest_base_path/queue/1/history$]);
+
+    is($links->[2]{ref}, 'history');
+    like($links->[2]{_url}, qr[$rest_base_path/queue/1/history$]);
+
+    is($links->[3]{ref}, 'create');
+    is($links->[3]{type}, 'ticket');
+    like($links->[3]{_url}, qr[$rest_base_path/ticket\?Queue=1$]);
 
     my $creator = $content->{Creator};
     is($creator->{id}, 'RT_System');
diff --git a/xt/ticket-customfields.t b/xt/ticket-customfields.t
index d276ff3..d414a3b 100644
--- a/xt/ticket-customfields.t
+++ b/xt/ticket-customfields.t
@@ -115,11 +115,13 @@ my ($ticket_url, $ticket_id);
         [{
             ref => 'customfield',
             id  => $single_cf_id,
+            name => 'Single',
             type => 'customfield',
             _url => re(qr[$rest_base_path/customfield/$single_cf_id$]),
         }, {
             ref => 'customfield',
             id  => $multi_cf_id,
+            name => 'Multi',
             type => 'customfield',
             _url => re(qr[$rest_base_path/customfield/$multi_cf_id$]),
         }],

commit e3b6480cc0454e0c617f7b5a7c78a1c4059a1bfc
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Aug 16 17:19:10 2019 -0400

    Provide Ticket and Transaction CFs for queue lookup
    
    Provide the list of ticket and transaction custom fields
    applied to the queue in the queue response.

diff --git a/lib/RT/Extension/REST2/Resource/Queue.pm b/lib/RT/Extension/REST2/Resource/Queue.pm
index febdd5a..fc53ea5 100644
--- a/lib/RT/Extension/REST2/Resource/Queue.pm
+++ b/lib/RT/Extension/REST2/Resource/Queue.pm
@@ -4,6 +4,7 @@ use warnings;
 
 use Moose;
 use namespace::autoclean;
+use RT::Extension::REST2::Util qw(expand_uid);
 
 extends 'RT::Extension::REST2::Resource::Record';
 with (
@@ -50,6 +51,48 @@ sub hypermedia_links {
     return $links;
 }
 
+around 'serialize' => sub {
+    my $orig = shift;
+    my $self = shift;
+    my $data = $self->$orig(@_);
+
+    # Load Ticket Custom Fields for this queue
+    if ( my $ticket_cfs = $self->record->TicketCustomFields ) {
+        my @values;
+        while (my $cf = $ticket_cfs->Next) {
+            my $entry = expand_uid($cf->UID);
+            my $content = {
+                %$entry,
+                ref      => 'customfield',
+                name     => $cf->Name,
+            };
+
+            push @values, $content;
+        }
+
+        $data->{TicketCustomFields} = \@values;
+    }
+
+    # Load Transaction custom fields for this queue
+    if ( my $ticket_cfs = $self->record->TicketTransactionCustomFields ) {
+        my @values;
+        while (my $cf = $ticket_cfs->Next) {
+            my $entry = expand_uid($cf->UID);
+            my $content = {
+                %$entry,
+                ref      => 'customfield',
+                name     => $cf->Name,
+            };
+
+            push @values, $content;
+        }
+
+        $data->{TicketTransactionCustomFields} = \@values;
+    }
+
+    return $data;
+};
+
 __PACKAGE__->meta->make_immutable;
 
 1;
diff --git a/xt/queues.t b/xt/queues.t
index 1a4ec4e..e27bcd9 100644
--- a/xt/queues.t
+++ b/xt/queues.t
@@ -20,6 +20,19 @@ ok($ok, $msg);
 ok($ok, $msg);
 my $single_cf_id = $single_cf->Id;
 
+my $single_ticket_cf = RT::CustomField->new( RT->SystemUser );
+($ok, $msg) = $single_ticket_cf->Create( Name => 'SingleTicket', Type => 'FreeformSingle', Queue => $queue_obj->Id );
+ok($ok, $msg);
+my $single_ticket_cf_id = $single_ticket_cf->Id;
+
+my $single_txn_cf = RT::CustomField->new( RT->SystemUser );
+($ok, $msg) = $single_txn_cf->Create( Name => 'SingleTxn', Type => 'FreeformSingle', LookupType => RT::Transaction->CustomFieldLookupType );
+ok($ok, $msg);
+
+($ok, $msg) = $single_txn_cf->AddToObject($queue_obj);
+ok($ok, $msg);
+my $single_txn_cf_id = $single_txn_cf->Id;
+
 my $queue_url;
 # search Name = General
 {
@@ -96,6 +109,12 @@ my $queue_url;
     is_deeply($content->{Cc}, [], 'no Ccs set');
     is_deeply($content->{AdminCc}, [], 'no AdminCcs set');
 
+    my $tix_cfs = $content->{TicketCustomFields};
+    is( $tix_cfs->[0]{id}, $single_ticket_cf_id, 'Returned custom field ' . $single_ticket_cf->Name . ' applied to queue' );
+
+    my $txn_cfs = $content->{TicketTransactionCustomFields};
+    is( $txn_cfs->[0]{id}, $single_txn_cf_id, 'Returned custom field ' . $single_txn_cf->Name . ' applied to queue' );
+
     ok(!exists($content->{Owner}), 'no Owner at the queue level');
     ok(!exists($content->{Requestor}), 'no Requestor at the queue level');
 }

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


More information about the Bps-public-commit mailing list