[Bps-public-commit] rt-extension-rest2 branch, add-validation-hook-for-status-updates-2, created. 1.09-12-g29ecabe

Dianne Skoll dianne at bestpractical.com
Mon Jan 4 14:47:53 EST 2021


The branch, add-validation-hook-for-status-updates-2 has been created
        at  29ecabe2d0d977766086b45cdb9a5ad0dbb75059 (commit)

- Log -----------------------------------------------------------------
commit 6c2026403ed9464cb55750c5d471819f614fa7b3
Author: Dianne Skoll <dianne at bestpractical.com>
Date:   Mon Jan 4 14:23:07 2021 -0500

    Add system for maintaining validation hooks to RT::Extension::REST2

diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index e11acfb..5bf6527 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -1250,6 +1250,48 @@ sub CleanupRequest {
             'DBIx::SearchBuilder::Record::Cachable' => 'FlushCache' ) );
 }
 
+# This hash holds all registered validation hooks.
+our $ValidationHooks = {};
+
+# Acceptable validation hook types
+my $ValidationHookTypes = { update => 1, create => 1 };
+
+# Acceptable validation hook objects
+my $ValidationHookObjects = { 'RT::Ticket' => 1 };
+
+# Register a validation hook for the given update type and object type
+# Returns a true value on successful registration; false otherwise.
+# TODO: Should probably carp if we cannot create the hook
+sub add_validation_hook
+{
+    my ($class, $type, $object, $coderef) = @_;
+
+    # Allow caller to pass in an object or a name like RT::Ticket;
+    $object = ref($object) if ref($object);
+
+    return undef unless exists($ValidationHookTypes->{$type});
+    return undef unless exists($ValidationHookObjects->{$object});
+
+    push(@{$ValidationHooks->{$type}->{$object}}, $coderef);
+    return 1;
+}
+
+# Call all of the validation hooks for the given update type
+# and object type.  Returns (1, "") if they all pass, or (0, "msg") if
+# one of them fails.  Each validation hook is expected to return an
+# ($ok, "msg") indicator.
+sub call_validation_hooks
+{
+    my $class = shift;
+    my $type = shift;
+    my $object = shift;
+    foreach my $hook (@{$ValidationHooks->{$type}->{$object}}) {
+        my ($ok, $msg) = $hook->(@_);
+        return (0, $msg) unless $ok;
+    }
+    return (1, "");
+}
+
 =head1 AUTHOR
 
 Best Practical Solutions, LLC <modules at bestpractical.com>

commit 25f56e6e6bdbd7205725755bc451654621c349eb
Author: Dianne Skoll <dianne at bestpractical.com>
Date:   Mon Jan 4 14:34:40 2021 -0500

    Add validation hooks for ticket update/create/correspond.

diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index f15d996..edd5c9e 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -116,6 +116,12 @@ sub add_message {
         Subject   => $args{Subject},
     );
 
+    # Call validation hooks
+    my ($ok, $errmsg) = RT::Extension::REST2->call_validation_hooks('update', 'RT::Ticket', $self->record, \%args);
+    if (!$ok) {
+        return (\400, $errmsg);
+    }
+
     # Process attachments
     foreach my $attachment (@{$args{Attachments}}) {
         $MIME->attach(
diff --git a/lib/RT/Extension/REST2/Resource/Ticket.pm b/lib/RT/Extension/REST2/Resource/Ticket.pm
index f8cf37a..de1c65c 100644
--- a/lib/RT/Extension/REST2/Resource/Ticket.pm
+++ b/lib/RT/Extension/REST2/Resource/Ticket.pm
@@ -12,7 +12,8 @@ with (
         => { -alias => { hypermedia_links => '_default_hypermedia_links' } },
     'RT::Extension::REST2::Resource::Record::Deletable',
     'RT::Extension::REST2::Resource::Record::Writable'
-        => { -alias => { create_record => '_create_record' } },
+    => { -alias => { create_record => '_create_record',
+                     update_record => '_update_record'} },
 );
 
 sub dispatch_rules {
@@ -52,10 +53,29 @@ sub create_record {
         );
     }
 
-    my ($ok, $txn, $msg) = $self->_create_record($data);
+    my ($ok, $txn, $msg);
+
+    # Call validation hooks and return failure if any fail
+    ($ok, $msg) = RT::Extension::REST2->call_validation_hooks('create', 'RT::Ticket', $queue, $data);
+    if (!$ok) {
+        return (\400, $msg);
+    }
+
+    ($ok, $txn, $msg) = $self->_create_record($data);
     return ($ok, $msg);
 }
 
+sub update_record
+{
+    my ($self, $data) = @_;
+
+    my ($ok, $msg) = RT::Extension::REST2->call_validation_hooks('update', 'RT::Ticket', $self->record, $data);
+    if (!$ok) {
+        return (0, $msg);
+    }
+    return $self->_update_record($data);
+}
+
 sub forbidden {
     my $self = shift;
     return 0 unless $self->record->id;

commit 29ecabe2d0d977766086b45cdb9a5ad0dbb75059
Author: Dianne Skoll <dianne at bestpractical.com>
Date:   Mon Jan 4 14:47:05 2021 -0500

    Add unit test to exercise ticket update/create validation hooks.

diff --git a/xt/ticket_validate.t b/xt/ticket_validate.t
new file mode 100644
index 0000000..6cb8ecd
--- /dev/null
+++ b/xt/ticket_validate.t
@@ -0,0 +1,97 @@
+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 $user = RT::Extension::REST2::Test->user;
+
+sub my_create_validator
+{
+    my ($queue, $data) = @_;
+    if ($data->{VALIDATE} && $data->{VALIDATE} eq 'BAD_CREATE!') {
+        return (0, 'Bad data');
+    }
+    return (1, '');
+}
+
+sub my_update_validator
+{
+    my ($ticket, $data) = @_;
+    if ( $data->{'VALIDATE'} and $data->{'VALIDATE'} eq 'BAD_UPDATE!' ) {
+        return (0, 'Bad data');
+    }
+
+    return (1, '');
+}
+
+ok(RT::Extension::REST2->add_validation_hook('create', 'RT::Ticket', sub { return my_create_validator(@_); }), 'Successfully added ticket-create validation hook');
+ok(RT::Extension::REST2->add_validation_hook('update', 'RT::Ticket', sub { return my_update_validator(@_); }), 'Successfully added ticket-update validation hook');
+
+ok(!RT::Extension::REST2->add_validation_hook('update', 'RT::NOPE', sub { return my_update_validator(@_); }), 'Correctly failed to add validation hook for unknown object type');
+
+ok(!RT::Extension::REST2->add_validation_hook('migrate', 'RT::Ticket', sub { return my_update_validator(@_); }), 'Correctly failed to add validation hook for unknown modification type');
+
+diag 'Check validation on create';
+my ($ticket_url, $ticket_id);
+{
+    $user->PrincipalObj->GrantRight( Right => 'CreateTicket' );
+    my $payload = {
+        Subject => 'Ticket creation using REST',
+        Queue   => 'General',
+        Content => 'Testing ticket creation using REST API.',
+        VALIDATE => 'BAD_CREATE!',
+    };
+
+    my $res = $mech->post_json("$rest_base_path/ticket",
+        $payload,
+        'Authorization' => $auth,
+    );
+    is($res->code, 400, 'Validation returned 400');
+
+    # Do a successful create for later test
+    $payload = {
+        Subject => 'Ticket creation using REST',
+        Queue   => 'General',
+        Content => 'Testing ticket creation using REST API.',
+    };
+
+    $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+)]);
+}
+
+# Ticket Update
+{
+    {
+        no warnings;
+        *RT::Extension::REST2::Resource::Ticket::validate_hook_before_create = *RT::Extension::REST2::TestValidate::validate_hook_before_create;
+        *RT::Extension::REST2::Resource::Ticket::validate_hook_before_update = *RT::Extension::REST2::TestValidate::validate_hook_before_update;
+    }
+
+    my $payload = {
+        Subject  => 'Ticket update using REST',
+        Priority => 42,
+        VALIDATE => 'BAD_UPDATE!',
+    };
+
+    $user->PrincipalObj->GrantRight( Right => 'ShowTicket' );
+    $user->PrincipalObj->GrantRight( Right => 'ModifyTicket' );
+
+    my $res = $mech->put_json($ticket_url,
+        $payload,
+        'Authorization' => $auth,
+        );
+    is($res->code, 200, 'Validation returned 200');
+    cmp_deeply($mech->json_response, [0, 'Bad data'], "Validation error is indicated by JSON response");
+
+}
+
+
+done_testing;

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


More information about the Bps-public-commit mailing list