[Bps-public-commit] rt-extension-rest2 branch, master, updated. 1.07-12-gcc69c20

? sunnavy sunnavy at bestpractical.com
Thu Apr 9 16:35:34 EDT 2020


The branch, master has been updated
       via  cc69c2078867935e7d5ea87bc96eea6deefc2dbf (commit)
       via  7cb6dbd2ce6b89f7ec5f4ae54460837df49ec4fa (commit)
       via  c9b0d872bb2ccd6d5099c25a7bca65dbd303aab5 (commit)
       via  8ce8a5a0496a9d6a35d4388d1100682f51435b39 (commit)
       via  d0180963a28e4e68e067ff560c2b32c2100383a0 (commit)
       via  910c3e08b654acdb66a0dc47c94e9e02d8ff2412 (commit)
      from  13d932fc5b31d8acb8b88e2edd19fe8b4ae26922 (commit)

Summary of changes:
 README                                             |  13 +-
 lib/RT/Extension/REST2.pm                          |  13 +-
 lib/RT/Extension/REST2/Resource/Message.pm         |  53 +++++-
 lib/RT/Extension/REST2/Resource/Queue.pm           |  43 +++++
 .../Extension/REST2/Resource/Record/Hypermedia.pm  |   1 +
 lib/RT/Extension/REST2/Resource/Record/Writable.pm |  84 +---------
 lib/RT/Extension/REST2/Util.pm                     |  80 +++++++++
 xt/asset-customfields.t                            |   2 +
 xt/queues.t                                        |  49 +++++-
 xt/ticket-customfields.t                           |  35 ++++
 xt/transaction-customfields.t                      | 178 +++++++++++++++++++++
 11 files changed, 458 insertions(+), 93 deletions(-)
 create mode 100644 xt/transaction-customfields.t

- Log -----------------------------------------------------------------
commit 910c3e08b654acdb66a0dc47c94e9e02d8ff2412
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 3868426..a01b94a 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;
@@ -325,7 +247,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 2474e83..b312e73 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
     ]]
 };
 
@@ -252,4 +254,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 d0180963a28e4e68e067ff560c2b32c2100383a0
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..5871ce5 100644
--- a/README
+++ b/README
@@ -275,11 +275,22 @@ USAGE
             -d '{ "Content": "Testing a correspondence", "ContentType": "text/plain" }'
             'https://myrt.com/REST/2.0/ticket/6/correspond'
 
-        # Comment a ticket
+        # 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 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 c4b61c6..e0315c5 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -311,11 +311,22 @@ 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'
 
-    # Comment a ticket
+    # 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 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 57c8ef1..720791c 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',
@@ -69,8 +70,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 +93,57 @@ sub add_message {
             \400, $msg || "Message failed for unknown reason");
     }
 
+    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;
 }
 
+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
+    my %txn_custom_fields;
+
+    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" );
+            push @results, "Unable to load transaction custom field: $cf_name";
+            next;
+        }
+
+        my $txn_input_name = RT::Interface::Web::GetCustomFieldInputName(
+                             CustomField => $cf_obj,
+                             Grouping    => undef
+        );
+
+        $txn_custom_fields{$txn_input_name} = $TxnCustomFields->{$cf_name};
+    }
+
+    # 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';
+        }
+    }
+
+    return @results;
+}
+
 sub create_path {
     my $self = shift;
     my $id = $self->created_transaction->Id;
diff --git a/xt/ticket-customfields.t b/xt/ticket-customfields.t
index 045e5fb..4916bb4 100644
--- a/xt/ticket-customfields.t
+++ b/xt/ticket-customfields.t
@@ -358,6 +358,37 @@ my $no_ticket_cf_values = bag(
     cmp_deeply($content->{CustomFields}, $modified_again_single_cf_value, '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
new file mode 100644
index 0000000..fc143ea
--- /dev/null
+++ b/xt/transaction-customfields.t
@@ -0,0 +1,77 @@
+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", "Custom fields updated" ], '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');
+}
+
+{
+    my @warnings;
+    local $SIG{__WARN__} = sub {
+        push @warnings, @_;
+    };
+    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);
+
+    is( scalar @warnings, 1, 'Got one warning' );
+    like(
+        $warnings[0],
+        qr/Unable to load transaction custom field: not a real CF name/,
+        'Got the unable to load warning'
+    );
+
+    is( $res->code, 201, 'Correspond response code is 201 because correspond succeeded');
+    is_deeply( $mech->json_response, [ "Correspondence added", "Unable to load transaction custom field: not a real CF name" ], 'Bogus cf name');
+}
+
+
+done_testing();

commit 8ce8a5a0496a9d6a35d4388d1100682f51435b39
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 3003665..acfc955 100644
--- a/xt/asset-customfields.t
+++ b/xt/asset-customfields.t
@@ -126,11 +126,13 @@ my $no_asset_cf_values = bag(
         [{
             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 4916bb4..326237f 100644
--- a/xt/ticket-customfields.t
+++ b/xt/ticket-customfields.t
@@ -176,11 +176,13 @@ my $no_ticket_cf_values = bag(
         [{
             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$]),
         }],
@@ -234,11 +236,13 @@ my $no_ticket_cf_values = bag(
             id  => $single_cf_id,
             type => 'customfield',
             _url => re(qr[$rest_base_path/customfield/$single_cf_id$]),
+            name => 'Single',
         }, {
             ref => 'customfield',
             id  => $multi_cf_id,
             type => 'customfield',
             _url => re(qr[$rest_base_path/customfield/$multi_cf_id$]),
+            name => 'Multi',
         }],
         'Two CF hypermedia',
     );

commit c9b0d872bb2ccd6d5099c25a7bca65dbd303aab5
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');
 }

commit 7cb6dbd2ce6b89f7ec5f4ae54460837df49ec4fa
Author: Andrew Ruthven <puck at catalystcloud.nz>
Date:   Sun Dec 1 21:00:17 2019 +0000

    Test seting Txn CF as a user, test retrieval of Txn CF via REST API

diff --git a/xt/transaction-customfields.t b/xt/transaction-customfields.t
index fc143ea..cb9edcf 100644
--- a/xt/transaction-customfields.t
+++ b/xt/transaction-customfields.t
@@ -74,4 +74,105 @@ is( $res->code, 200, 'Fetched ticket via REST2 API');
 }
 
 
+# Test as a user.
+my $user = RT::Extension::REST2::Test->user;
+
+$user->PrincipalObj->GrantRight( Right => 'CreateTicket' );
+$user->PrincipalObj->GrantRight( Right => 'ModifyTicket' );
+$user->PrincipalObj->GrantRight( Right => 'ReplyToTicket' );
+$user->PrincipalObj->GrantRight( Right => 'SeeQueue' );
+$user->PrincipalObj->GrantRight( Right => 'ShowTicket' );
+$user->PrincipalObj->GrantRight( Right => 'ShowTicketComments' );
+$user->PrincipalObj->GrantRight( Right => 'SeeCustomField' );
+$user->PrincipalObj->GrantRight( Right => 'ModifyCustomField' );
+
+my ($ticket_url, $ticket_id);
+{
+    my $payload = {
+        Subject => 'Ticket for CF test',
+        Queue   => 'General',
+        Content => 'Ticket for CF test content',
+    };
+
+    my $res = $mech->post_json("$rest_base_path/ticket",
+        $payload,
+        'Authorization' => $auth,
+    );
+    is($res->code, 201);
+    ok($ticket_url = $res->header('location'));
+    ok(($ticket_id) = $ticket_url =~ qr[/ticket/(\d+)]);
+
+    # We need the hypermedia URLs...
+    $res = $mech->get($ticket_url,
+        'Authorization' => $auth,
+    );
+    is($res->code, 200);
+
+    $payload = {
+        Subject => 'Add Txn with CF',
+        Content => 'Content',
+        ContentType => 'text/plain',
+        'TxnCustomFields' => {
+            'TxnCF' => 'Txn CustomField',
+         },
+    };
+
+    $res = $mech->post_json($mech->url_for_hypermedia('correspond'),
+        $payload,
+        'Authorization' => $auth,
+    );
+    is($res->code, 201);
+    my $response = $mech->json_response;
+
+
+    my $response_value = bag(
+        re(qr/Correspondence added|Message added/), 'Custom fields updated',
+    );
+
+    cmp_deeply($mech->json_response, $response_value, 'Response containts correct strings');
+}
+
+# Look for the Transaction with our CustomField set.
+{
+    my $res = $mech->get($ticket_url,
+        'Authorization' => $auth,
+    );
+    is($res->code, 200);
+
+    $res = $mech->get($mech->url_for_hypermedia('history'),
+        'Authorization' => $auth,
+    );
+    is($res->code, 200);
+
+    my $content = $mech->json_response;
+    is($content->{count}, 3);
+    is($content->{page}, 1);
+    is($content->{per_page}, 20);
+    is($content->{total}, 3);
+    is(scalar @{$content->{items}}, 3);
+
+    # Check the correspond txn (0 = create, 1 = correspond)
+    my $txn = @{ $content->{items} }[1];
+
+    $res = $mech->get($txn->{_url},
+        'Authorization' => $auth,
+    );
+    is($res->code, 200);
+
+    $content = $mech->json_response;
+    like($content->{Data}, qr/^Add Txn with CF/);
+
+    cmp_deeply(
+        $content->{CustomFields},
+        [   {   'values' => ['Txn CustomField'],
+                'type'   => 'customfield',
+                'id'     => $cfid,
+                '_url'   => ignore(),
+                'name'   => 'TxnCF',
+            }
+        ],
+        'Txn is set'
+    );
+}
+
 done_testing();

commit cc69c2078867935e7d5ea87bc96eea6deefc2dbf
Merge: 13d932f 7cb6dbd
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Fri Apr 10 04:27:24 2020 +0800

    Merge branch 'transaction-cfs'


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


More information about the Bps-public-commit mailing list