[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