[Bps-public-commit] rt-extension-rest2 branch, rest2_api_custom_field_admin, updated. 1.07-9-gf3d73eb
Aaron Trevena
ast at bestpractical.com
Tue Apr 7 08:12:10 EDT 2020
The branch, rest2_api_custom_field_admin has been updated
via f3d73ebc2b8831ea2019214053dca36c1cf7cb78 (commit)
via 1233ed1a6ac9a85b4e66d71f3c52498cbb350aed (commit)
via 10ed40cf359a8f690b4fcb4c1cf937ffc61edfee (commit)
via 89628cdaf85497a0079bb7097f7fab4c15189045 (commit)
via 9afc1ec286491cf3f26da3e57207655b67029c1f (commit)
via c9aafdfb7248a6ed4137bfe4f619b06a33d51b57 (commit)
from 5abdab89323930fd1a7924722bd7f532b09beaea (commit)
Summary of changes:
MANIFEST | 3 +
README | 25 +++
lib/RT/Extension/REST2.pm | 39 ++++
lib/RT/Extension/REST2/Resource/CustomField.pm | 34 ++-
.../Extension/REST2/Resource/CustomFieldValue.pm | 106 ++++++++++
.../Extension/REST2/Resource/CustomFieldValues.pm | 66 ++++++
lib/RT/Extension/REST2/Resource/CustomFields.pm | 27 +++
t/customfields.t | 86 ++++++++
xt/customfields.t | 171 ++++++++++++++-
xt/customfieldvalues.t | 232 +++++++++++++++++++++
10 files changed, 778 insertions(+), 11 deletions(-)
create mode 100644 lib/RT/Extension/REST2/Resource/CustomFieldValue.pm
create mode 100644 lib/RT/Extension/REST2/Resource/CustomFieldValues.pm
create mode 100644 t/customfields.t
create mode 100644 xt/customfieldvalues.t
- Log -----------------------------------------------------------------
commit c9aafdfb7248a6ed4137bfe4f619b06a33d51b57
Author: gibus <gibus at easter-eggs.com>
Date: Mon Oct 8 13:28:33 2018 +0200
Add all CRUD operations for CustomField
Based on public github PR #12
diff --git a/README b/README
index 40138e3..a77e886 100644
--- a/README
+++ b/README
@@ -482,9 +482,18 @@ USAGE
POST /customfields
search for custom fields using L</JSON searches> syntax
+ POST /customfield
+ create a customfield; provide JSON content
+
GET /customfield/:id
retrieve a custom field
+ PUT /customfield/:id
+ update a custom field's metadata; provide JSON content
+
+ DELETE /customfield/:id
+ disable customfield
+
Custom Roles
GET /customroles?query=<JSON>
POST /customroles
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 827f469..8723db8 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -528,12 +528,21 @@ Below are some examples using the endpoints above.
POST /customfields
search for custom fields using L</JSON searches> syntax
+ POST /customfield
+ create a customfield; provide JSON content
+
GET /customfield/:id
retrieve a custom field, with values if type is Select
GET /customfield/:id?category=<category name>
retrieve a custom field, with values filtered by category if type is Select
+ PUT /customfield/:id
+ update a custom field's metadata; provide JSON content
+
+ DELETE /customfield/:id
+ disable customfield
+
=head3 Custom Roles
GET /customroles?query=<JSON>
diff --git a/lib/RT/Extension/REST2/Resource/CustomField.pm b/lib/RT/Extension/REST2/Resource/CustomField.pm
index ddc9aa7..bd927c6 100644
--- a/lib/RT/Extension/REST2/Resource/CustomField.pm
+++ b/lib/RT/Extension/REST2/Resource/CustomField.pm
@@ -8,7 +8,9 @@ use namespace::autoclean;
extends 'RT::Extension::REST2::Resource::Record';
with 'RT::Extension::REST2::Resource::Record::Readable',
=> { -alias => { serialize => '_default_serialize' } },
- 'RT::Extension::REST2::Resource::Record::Hypermedia';
+ 'RT::Extension::REST2::Resource::Record::Hypermedia',
+ 'RT::Extension::REST2::Resource::Record::DeletableByDisabling',
+ 'RT::Extension::REST2::Resource::Record::Writable';
sub dispatch_rules {
Path::Dispatcher::Rule::Regex->new(
@@ -36,6 +38,21 @@ sub serialize {
return $data;
}
+sub forbidden {
+ my $self = shift;
+ my $method = $self->request->method;
+ if ($self->record->id) {
+ if ($method eq 'GET') {
+ return !$self->record->CurrentUserHasRight('SeeCustomField');
+ } else {
+ return !($self->record->CurrentUserHasRight('SeeCustomField') && $self->record->CurrentUserHasRight('AdminCustomField'));
+ }
+ } else {
+ return !$self->current_user->HasRight(Right => "AdminCustomField", Object => RT->System);
+ }
+ return 0;
+}
+
__PACKAGE__->meta->make_immutable;
1;
commit 9afc1ec286491cf3f26da3e57207655b67029c1f
Author: gibus <gibus at easter-eggs.com>
Date: Mon Oct 8 13:29:11 2018 +0200
Add tests for all CRUD operations for CustomField
Based on public github PR #12
diff --git a/xt/customfields.t b/xt/customfields.t
index 766e153..396bfb0 100644
--- a/xt/customfields.t
+++ b/xt/customfields.t
@@ -8,10 +8,6 @@ my $auth = RT::Extension::REST2::Test->authorization_header;
my $rest_base_path = '/REST/2.0';
my $user = RT::Extension::REST2::Test->user;
-my $freeform_cf = RT::CustomField->new(RT->SystemUser);
-$freeform_cf->Create(Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1, Queue => 'General');
-my $freeform_cf_id = $freeform_cf->id;
-
my $select_cf = RT::CustomField->new(RT->SystemUser);
$select_cf->Create(Name => 'Select CF', Type => 'Select', MaxValues => 1, Queue => 'General');
$select_cf->AddValue(Name => 'First Value', SortOder => 0);
@@ -27,8 +23,58 @@ $basedon_cf->AddValue(Name => 'With No Value', SortOder => 0);
my $basedon_cf_id = $basedon_cf->id;
my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
+my $freeform_cf;
+my $freeform_cf_id;
+
+# Right test - create customfield without SeeCustomField nor AdminCustomField
+{
+ my $payload = {
+ Name => 'Freeform CF',
+ Type => 'Freeform',
+ MaxValues => 1,
+ };
+ my $res = $mech->post_json("$rest_base_path/customfield",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 403);
+ is($res->message, 'Forbidden');
+
+ my $freeform_cf = RT::CustomField->new(RT->SystemUser);
+ my ($ok, $msg) = $freeform_cf->Load('Freeform CF');
+ is($freeform_cf->id, undef);
+ ok(!$ok);
+ is($msg, 'Not found');
+}
+
+# Customfield create
+{
+ $user->PrincipalObj->GrantRight( Right => 'SeeCustomField' );
+ $user->PrincipalObj->GrantRight( Right => 'AdminCustomField' );
+ my $payload = {
+ Name => 'Freeform CF',
+ Type => 'Freeform',
+ LookupType => 'RT::Queue-RT::Ticket',
+ MaxValues => 1,
+ };
+ my $res = $mech->post_json("$rest_base_path/customfield",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 201);
+
+ $freeform_cf = RT::CustomField->new(RT->SystemUser);
+ $freeform_cf->Load('Freeform CF');
+ $freeform_cf_id = $freeform_cf->id;
+ is($freeform_cf->id, 4);
+ is($freeform_cf->Description, '');
+}
+
+
# Right test - search all tickets customfields without SeeCustomField
{
+ $user->PrincipalObj->RevokeRight( Right => 'SeeCustomField' );
+
my $res = $mech->post_json("$rest_base_path/customfields",
[{field => 'LookupType', value => 'RT::Queue-RT::Ticket'}],
'Authorization' => $auth,
@@ -56,7 +102,7 @@ my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
is($content->{count}, 3);
my $items = $content->{items};
is(scalar(@$items), 3);
-
+
is($items->[0]->{type}, 'customfield');
is($items->[0]->{id}, $freeform_cf->id);
like($items->[0]->{_url}, qr{$rest_base_path/customfield/$freeform_cf_id$});
@@ -217,5 +263,99 @@ my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
is_deeply($values, ['With No Value']);
}
+
+# Display customfield
+{
+ $user->PrincipalObj->GrantRight( Right => 'SeeCustomField' );
+
+ my $res = $mech->get("$rest_base_path/customfield/$freeform_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+ my $content = $mech->json_response;
+ is($content->{id}, $freeform_cf_id);
+ is($content->{Name}, 'Freeform CF');
+ is($content->{Description}, '');
+ is($content->{LookupType}, 'RT::Queue-RT::Ticket');
+ is($content->{Type}, 'Freeform');
+ is($content->{MaxValues}, 1);
+ is($content->{Disabled}, 0);
+
+ my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
+ push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
+ ok(exists $content->{$_}, "got $_") for @fields;
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 1);
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $freeform_cf_id);
+ is($links->[0]{type}, 'customfield');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$freeform_cf_id$});
+}
+
+# Right test - update customfield without AdminCustomField
+{
+ $user->PrincipalObj->RevokeRight( Right => 'AdminCustomField' );
+
+ my $payload = {
+ Description => 'This is a CF for testing REST CRUD on CFs',
+ };
+ my $res = $mech->put_json("$rest_base_path/customfield/$freeform_cf_id",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 403);
+ is($res->message, 'Forbidden');
+}
+
+# Update customfield
+{
+ $user->PrincipalObj->GrantRight( Right => 'AdminCustomField' );
+
+ my $payload = {
+ Description => 'This is a CF for testing REST CRUD on CFs',
+ };
+ my $res = $mech->put_json("$rest_base_path/customfield/$freeform_cf_id",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $freeform_cf = RT::CustomField->new(RT->SystemUser);
+ $freeform_cf->Load('Freeform CF');
+ is($freeform_cf->id, $freeform_cf_id);
+ is($freeform_cf->Description, 'This is a CF for testing REST CRUD on CFs');
+}
+
+# Right test - delete customfield without AdminCustomField
+{
+ $user->PrincipalObj->RevokeRight( Right => 'AdminCustomField' );
+
+ my $res = $mech->delete("$rest_base_path/customfield/$freeform_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 403);
+ is($res->message, 'Forbidden');
+
+ my $freeform_cf = RT::CustomField->new(RT->SystemUser);
+ $freeform_cf->Load('Freeform CF');
+ is($freeform_cf->Disabled, 0);
+}
+
+# Delete customfield
+{
+ $user->PrincipalObj->GrantRight( Right => 'AdminCustomField' );
+
+ my $res = $mech->delete("$rest_base_path/customfield/$freeform_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 204);
+
+ my $freeform_cf = RT::CustomField->new(RT->SystemUser);
+ $freeform_cf->Load('Freeform CF');
+ is($freeform_cf->Disabled, 1);
+}
+
+
done_testing;
commit 89628cdaf85497a0079bb7097f7fab4c15189045
Author: gibus <gibus at easter-eggs.com>
Date: Tue Oct 9 13:55:53 2018 +0200
Add customfieldvalue(s) endpoints
Add customfieldvalues endpoints in hypermedia links of customfield
Based on public github PR #13
diff --git a/README b/README
index a77e886..44ad0b1 100644
--- a/README
+++ b/README
@@ -493,6 +493,22 @@ USAGE
DELETE /customfield/:id
disable customfield
+ Custom Field Values
+ GET /customfield/:id/values?query=<JSON>
+ POST /customfield/:id/values
+ search for values of a custom field using L</JSON searches> syntax
+
+ POST /customfield/:id/value
+ add a value to a custom field; provide JSON content
+
+ GET /customfield/:id/value/:id
+ retrieve a value of a custom field
+
+ PUT /customfield/:id/value/:id
+ update a value of a custom field; provide JSON content
+
+ DELETE /customfield/:id/value/:id
+ remove a value from a custom field
Custom Roles
GET /customroles?query=<JSON>
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 8723db8..7b17587 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -543,6 +543,24 @@ Below are some examples using the endpoints above.
DELETE /customfield/:id
disable customfield
+=head3 Custom Field Values
+
+ GET /customfield/:id/values?query=<JSON>
+ POST /customfield/:id/values
+ search for values of a custom field using L</JSON searches> syntax
+
+ POST /customfield/:id/value
+ add a value to a custom field; provide JSON content
+
+ GET /customfield/:id/value/:id
+ retrieve a value of a custom field
+
+ PUT /customfield/:id/value/:id
+ update a value of a custom field; provide JSON content
+
+ DELETE /customfield/:id/value/:id
+ remove a value from a custom field
+
=head3 Custom Roles
GET /customroles?query=<JSON>
diff --git a/lib/RT/Extension/REST2/Resource/CustomField.pm b/lib/RT/Extension/REST2/Resource/CustomField.pm
index bd927c6..399c5b5 100644
--- a/lib/RT/Extension/REST2/Resource/CustomField.pm
+++ b/lib/RT/Extension/REST2/Resource/CustomField.pm
@@ -8,7 +8,8 @@ use namespace::autoclean;
extends 'RT::Extension::REST2::Resource::Record';
with 'RT::Extension::REST2::Resource::Record::Readable',
=> { -alias => { serialize => '_default_serialize' } },
- 'RT::Extension::REST2::Resource::Record::Hypermedia',
+ 'RT::Extension::REST2::Resource::Record::Hypermedia'
+ => { -alias => { hypermedia_links => '_default_hypermedia_links' } },
'RT::Extension::REST2::Resource::Record::DeletableByDisabling',
'RT::Extension::REST2::Resource::Record::Writable';
@@ -53,8 +54,20 @@ sub forbidden {
return 0;
}
+sub hypermedia_links {
+ my $self = shift;
+ my $links = $self->_default_hypermedia_links(@_);
+
+ if ($self->record->IsSelectionType) {
+ push @$links, {
+ ref => 'customfieldvalues',
+ _url => RT::Extension::REST2->base_uri . "/customfield/" . $self->record->id . "/values",
+ };
+ }
+ return $links;
+}
+
__PACKAGE__->meta->make_immutable;
1;
-
diff --git a/lib/RT/Extension/REST2/Resource/CustomFieldValue.pm b/lib/RT/Extension/REST2/Resource/CustomFieldValue.pm
new file mode 100644
index 0000000..5be6acb
--- /dev/null
+++ b/lib/RT/Extension/REST2/Resource/CustomFieldValue.pm
@@ -0,0 +1,106 @@
+package RT::Extension::REST2::Resource::CustomFieldValue;
+use strict;
+use warnings;
+
+use Moose;
+use namespace::autoclean;
+use RT::Extension::REST2::Util qw(expand_uid);
+
+extends 'RT::Extension::REST2::Resource::Record';
+with 'RT::Extension::REST2::Resource::Record::Readable',
+ 'RT::Extension::REST2::Resource::Record::Hypermedia',
+ 'RT::Extension::REST2::Resource::Record::Deletable',
+ 'RT::Extension::REST2::Resource::Record::Writable';
+
+has 'customfield' => (
+ is => 'ro',
+ isa => 'RT::CustomField',
+);
+
+sub dispatch_rules {
+ Path::Dispatcher::Rule::Regex->new(
+ regex => qr{^/customfield/(\d+)/value/?$},
+ block => sub {
+ my ($match, $req) = @_;
+ my $cf_id = $match->pos(1);
+ my $cf = RT::CustomField->new($req->env->{"rt.current_user"});
+ $cf->Load($cf_id);
+ return { record_class => 'RT::CustomFieldValue', customfield => $cf }
+ },
+ ),
+ Path::Dispatcher::Rule::Regex->new(
+ regex => qr{^/customfield/(\d+)/value/(\d+)/?$},
+ block => sub {
+ my ($match, $req) = @_;
+ my $cf_id = $match->pos(1);
+ my $cf = RT::CustomField->new($req->env->{"rt.current_user"});
+ $cf->Load($cf_id);
+ return { record_class => 'RT::CustomFieldValue', record_id => shift->pos(2), customfield => $cf }
+ },
+ )
+}
+
+sub forbidden {
+ my $self = shift;
+ my $method = $self->request->method;
+ if ($method eq 'GET') {
+ return !$self->customfield->CurrentUserHasRight('SeeCustomField');
+ } else {
+ return !($self->customfield->CurrentUserHasRight('AdminCustomField') ||$self->customfield->CurrentUserHasRight('AdminCustomFieldValues'));
+ }
+}
+
+sub create_record {
+ my $self = shift;
+ my $data = shift;
+
+ my ($ok, $msg) = $self->customfield->AddValue(%$data);
+ $self->record->Load($ok) if $ok;
+ return ($ok, $msg);
+}
+
+sub delete_resource {
+ my $self = shift;
+
+ my ($ok, $msg) = $self->customfield->DeleteValue($self->record->id);
+ return $ok;
+}
+
+sub hypermedia_links {
+ my $self = shift;
+ my $record = $self->record;
+ my $cf = $self->customfield;
+
+ my $class = blessed($record);
+ $class =~ s/^RT:://;
+ $class = lc $class;
+ my $id = $record->id;
+
+ my $cf_class = blessed($cf);
+ $cf_class =~ s/^RT:://;
+ $cf_class = lc $cf_class;
+ my $cf_id = $cf->id;
+
+ my $cf_entry = expand_uid($cf->UID);
+
+ my $links = [
+ {
+ ref => 'self',
+ type => $class,
+ id => $id,
+ _url => RT::Extension::REST2->base_uri . "/$cf_class/$cf_id/$class/$id",
+ },
+ {
+ %$cf_entry,
+ ref => 'customfield',
+ },
+ ];
+
+ return $links;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+
diff --git a/lib/RT/Extension/REST2/Resource/CustomFieldValues.pm b/lib/RT/Extension/REST2/Resource/CustomFieldValues.pm
new file mode 100644
index 0000000..7fd94c8
--- /dev/null
+++ b/lib/RT/Extension/REST2/Resource/CustomFieldValues.pm
@@ -0,0 +1,66 @@
+package RT::Extension::REST2::Resource::CustomFieldValues;
+use strict;
+use warnings;
+
+use Moose;
+use namespace::autoclean;
+
+extends 'RT::Extension::REST2::Resource::Collection';
+with 'RT::Extension::REST2::Resource::Collection::QueryByJSON';
+
+has 'customfield' => (
+ is => 'ro',
+ isa => 'RT::CustomField',
+);
+
+sub dispatch_rules {
+ Path::Dispatcher::Rule::Regex->new(
+ regex => qr{^/customfield/(\d+)/values/?$},
+ block => sub {
+ my ($match, $req) = @_;
+ my $cf_id = $match->pos(1);
+ my $cf = RT::CustomField->new($req->env->{"rt.current_user"});
+ $cf->Load($cf_id);
+ my $values = $cf->Values;
+ return { customfield => $cf, collection => $values }
+ },
+ )
+}
+
+sub forbidden {
+ my $self = shift;
+ my $method = $self->request->method;
+ if ($method eq 'GET') {
+ return !$self->customfield->CurrentUserHasRight('SeeCustomField');
+ } else {
+ return !($self->customfield->CurrentUserHasRight('AdminCustomField') ||$self->customfield->CurrentUserHasRight('AdminCustomFieldValues'));
+ }
+}
+
+sub serialize {
+ my $self = shift;
+ my $collection = $self->collection;
+ my $cf = $self->customfield;
+ my @results;
+
+ while (my $item = $collection->Next) {
+ my $result = {
+ type => 'customfieldvalue',
+ id => $item->id,
+ name => $item->Name,
+ _url => RT::Extension::REST2->base_uri . "/customfield/" . $cf->id . '/value/' . $item->id,
+ };
+ push @results, $result;
+ }
+ return {
+ count => scalar(@results) + 0,
+ total => $collection->CountAll + 0,
+ per_page => $collection->RowsPerPage + 0,
+ page => ($collection->FirstRow / $collection->RowsPerPage) + 1,
+ items => \@results,
+ };
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
commit 10ed40cf359a8f690b4fcb4c1cf937ffc61edfee
Author: gibus <gibus at easter-eggs.com>
Date: Tue Oct 9 13:56:57 2018 +0200
Add tests for customfieldvalue(s) REST2 API endpoints
Based on public github PR #13
diff --git a/MANIFEST b/MANIFEST
index 8ba75b7..bbfc28e 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -33,6 +33,8 @@ lib/RT/Extension/REST2/Resource/Collection/ProcessPOSTasGET.pm
lib/RT/Extension/REST2/Resource/Collection/QueryByJSON.pm
lib/RT/Extension/REST2/Resource/CustomField.pm
lib/RT/Extension/REST2/Resource/CustomFields.pm
+lib/RT/Extension/REST2/Resource/CustomFieldValue.pm
+lib/RT/Extension/REST2/Resource/CustomFieldValues.pm
lib/RT/Extension/REST2/Resource/CustomRole.pm
lib/RT/Extension/REST2/Resource/CustomRoles.pm
lib/RT/Extension/REST2/Resource/Group.pm
@@ -71,6 +73,7 @@ xt/assets.t
xt/catalogs.t
xt/conflict.t
xt/customfields.t
+xt/customfieldvalues.t
xt/group-members.t
xt/not_found.t
xt/organization.t
diff --git a/xt/customfields.t b/xt/customfields.t
index 396bfb0..fec838d 100644
--- a/xt/customfields.t
+++ b/xt/customfields.t
@@ -163,12 +163,15 @@ my $freeform_cf_id;
ok(exists $content->{$_}, "got $_") for @fields;
my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
+ is(scalar @$links, 2);
is($links->[0]{ref}, 'self');
is($links->[0]{id}, $select_cf_id);
is($links->[0]{type}, 'customfield');
like($links->[0]{_url}, qr{$rest_base_path/customfield/$select_cf_id$});
+ is($links->[1]{ref}, 'customfieldvalues');
+ like($links->[1]{_url}, qr{$rest_base_path/customfield/$select_cf_id/values$});
+
my $values = $content->{Values};
is_deeply($values, ['First Value', 'Second Value', 'Third Value']);
}
@@ -193,12 +196,15 @@ my $freeform_cf_id;
ok(exists $content->{$_}, "got $_") for @fields;
my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
+ is(scalar @$links, 2);
is($links->[0]{ref}, 'self');
is($links->[0]{id}, $basedon_cf_id);
is($links->[0]{type}, 'customfield');
like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+ is($links->[1]{ref}, 'customfieldvalues');
+ like($links->[1]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id/values$});
+
my $values = $content->{Values};
is_deeply($values, ['With First Value', 'With No Value']);
}
@@ -223,12 +229,15 @@ my $freeform_cf_id;
ok(exists $content->{$_}, "got $_") for @fields;
my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
+ is(scalar @$links, 2);
is($links->[0]{ref}, 'self');
is($links->[0]{id}, $basedon_cf_id);
is($links->[0]{type}, 'customfield');
like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+ is($links->[1]{ref}, 'customfieldvalues');
+ like($links->[1]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id/values$});
+
my $values = $content->{Values};
is_deeply($values, ['With First Value']);
}
@@ -253,12 +262,16 @@ my $freeform_cf_id;
ok(exists $content->{$_}, "got $_") for @fields;
my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
+ is(scalar @$links, 2);
is($links->[0]{ref}, 'self');
is($links->[0]{id}, $basedon_cf_id);
is($links->[0]{type}, 'customfield');
like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+
+ is($links->[1]{ref}, 'customfieldvalues');
+ like($links->[1]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id/values$});
+
my $values = $content->{Values};
is_deeply($values, ['With No Value']);
}
diff --git a/xt/customfieldvalues.t b/xt/customfieldvalues.t
new file mode 100644
index 0000000..f691261
--- /dev/null
+++ b/xt/customfieldvalues.t
@@ -0,0 +1,232 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use RT::Extension::REST2::Test tests => undef;
+
+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;
+
+my $select_cf = RT::CustomField->new(RT->SystemUser);
+$select_cf->Create(Name => 'Select CF', Type => 'Select', MaxValues => 1);
+$select_cf->AddValue(Name => 'First Value', SortOrder => 0);
+$select_cf->AddValue(Name => 'Second Value', SortOrder => 1);
+$select_cf->AddValue(Name => 'Third Value', SortOrder => 2);
+my $select_cf_id = $select_cf->id;
+my $select_cf_values = $select_cf->Values->ItemsArrayRef;
+
+my $basedon_cf = RT::CustomField->new(RT->SystemUser);
+$basedon_cf->Create(Name => 'SubSelect CF', Type => 'Select', MaxValues => 1, BasedOn => $select_cf->id);
+$basedon_cf->AddValue(Name => 'With First Value', Category => $select_cf_values->[0]->Name, SortOder => 0);
+$basedon_cf->AddValue(Name => 'With No Value', SortOder => 0);
+my $basedon_cf_id = $basedon_cf->id;
+my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
+
+# Right test - retrieve all values without SeeCustomField
+{
+ my $res = $mech->get("$rest_base_path/customfield/$select_cf_id/values",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 403);
+}
+
+$user->PrincipalObj->GrantRight(Right => 'SeeCustomField');
+
+# Retrieve customfield's hypermedia link for customfieldvalues
+{
+ my $res = $mech->get("$rest_base_path/customfield/$select_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ my $links = $content->{_hyperlinks};
+ my @cfvs_links = grep { $_->{ref} eq 'customfieldvalues' } @$links;
+ is(scalar(@cfvs_links), 1);
+ like($cfvs_links[0]->{_url}, qr{$rest_base_path/customfield/$select_cf_id/values$});
+}
+
+# No customfieldvalues hypermedia link for non-select customfield
+{
+ my $freeform_cf = RT::CustomField->new(RT->SystemUser);
+ $freeform_cf->Create(Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1, Queue => 'General');
+ my $freeform_cf_id = $freeform_cf->id;
+
+ my $res = $mech->get("$rest_base_path/customfield/$freeform_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ my $links = $content->{_hyperlinks};
+ my @cfvs_links = grep { $_->{ref} eq 'customfieldvalues' } @$links;
+ is(scalar(@cfvs_links), 0);
+}
+
+# Retrieve all values
+{
+
+ my $res = $mech->get("$rest_base_path/customfield/$select_cf_id/values",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ is($content->{total}, 3);
+ is($content->{count}, 3);
+ my $items = $content->{items};
+ is(scalar(@$items), 3);
+
+ for (my $i=0; $i < scalar @$items; $i++) {
+ my $cf_value_id = $select_cf_values->[$i]->id;
+ is($items->[$i]->{type}, 'customfieldvalue');
+ is($items->[$i]->{id}, $cf_value_id);
+ is($items->[$i]->{name}, $select_cf_values->[$i]->Name);
+ like($items->[$i]->{_url}, qr{$rest_base_path/customfield/$select_cf_id/value/$cf_value_id$});
+ }
+}
+
+# Right test - udpate a value without AdminCustomFieldValues nor AdminCustomField
+{
+ my $payload = {
+ Name => 'Third and Last Value',
+ };
+ my $res = $mech->put_json("$rest_base_path/customfield/$select_cf_id/value/" . $select_cf_values->[-1]->id,
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 403);
+
+ $select_cf_values = $select_cf->Values->ItemsArrayRef;
+ is($select_cf_values->[-1]->Name, 'Third Value');
+}
+
+# Right test - udpate a value without AdminCustomFieldValues but with AdminCustomField
+{
+ $user->PrincipalObj->GrantRight(Right => 'AdminCustomField');
+
+ my $payload = {
+ Name => 'Third and Last Value',
+ };
+ my $res = $mech->put_json("$rest_base_path/customfield/$select_cf_id/value/" . $select_cf_values->[-1]->id,
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ $select_cf_values = $select_cf->Values->ItemsArrayRef;
+ is($select_cf_values->[-1]->Name, 'Third and Last Value');
+}
+
+# Right test - udpate a value without AdminCustomField but with AdminCustomFieldValues
+{
+ $user->PrincipalObj->RevokeRight(Right => 'AdminCustomField');
+ $user->PrincipalObj->GrantRight(Right => 'AdminCustomFieldValues', Object => $select_cf);
+ $user->PrincipalObj->GrantRight(Right => 'AdminCustomFieldValues', Object => $basedon_cf);
+
+ my $payload = {
+ Name => 'Third and Last but NOT Least Value',
+ };
+ my $res = $mech->put_json("$rest_base_path/customfield/$select_cf_id/value/" . $select_cf_values->[-1]->id,
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ $select_cf_values = $select_cf->Values->ItemsArrayRef;
+ is($select_cf_values->[-1]->Name, 'Third and Last but NOT Least Value');
+}
+
+# Add a value
+{
+ my $payload = {
+ Name => 'Fourth Value',
+ SortOrder => 3,
+ };
+ my $res = $mech->post_json("$rest_base_path/customfield/$select_cf_id/value",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 201);
+
+ $select_cf_values = $select_cf->Values->ItemsArrayRef;
+ is(scalar(@$select_cf_values), 4);
+ is($select_cf_values->[-1]->Name, 'Fourth Value');
+}
+
+# Retrieve a value
+{
+ my $cfv = $select_cf_values->[-2];
+ my $cfv_id = $cfv->id;
+ my $res = $mech->get("$rest_base_path/customfield/$select_cf_id/value/$cfv_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ foreach my $field (qw/id Name Description SortOrder Category/) {
+ is($content->{$field}, $cfv->$field);
+ }
+
+ ok(exists $content->{$_}, "got $_") for qw/Created Creator LastUpdated LastUpdatedBy/;
+
+ is($content->{CustomField}->{id}, $select_cf_id);
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 2);
+
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $cfv_id);
+ is($links->[0]{type}, 'customfieldvalue');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$select_cf_id/customfieldvalue/$cfv_id$});
+
+ is($links->[1]{ref}, 'customfield');
+ is($links->[1]{id}, $select_cf_id);
+ is($links->[1]{type}, 'customfield');
+ like($links->[1]{_url}, qr{$rest_base_path/customfield/$select_cf_id$});
+}
+
+# Retrieve all values filtered by category
+{
+ my $payload = [
+ {
+ field => 'Category',
+ value => $select_cf_values->[0]->Name,
+ }
+ ];
+ my $res = $mech->post_json("$rest_base_path/customfield/$basedon_cf_id/values",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ is($content->{total}, 1);
+ is($content->{count}, 1);
+ my $items = $content->{items};
+ is(scalar(@$items), 1);
+
+ for (my $i=0; $i < scalar @$items; $i++) {
+ my $cf_value_id = $basedon_cf_values->[$i]->id;
+ is($items->[$i]->{type}, 'customfieldvalue');
+ is($items->[$i]->{id}, $cf_value_id);
+ is($items->[$i]->{name}, $basedon_cf_values->[$i]->Name);
+ like($items->[$i]->{_url}, qr{$rest_base_path/customfield/$basedon_cf_id/value/$cf_value_id$});
+ }
+}
+
+# Delete a value
+{
+ my $res = $mech->delete("$rest_base_path/customfield/$select_cf_id/value/" . $select_cf_values->[-1]->id,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 204);
+
+ $select_cf_values = $select_cf->Values->ItemsArrayRef;
+ is(scalar(@$select_cf_values), 3);
+ is($select_cf_values->[-1]->Name, 'Third and Last but NOT Least Value');
+}
+
+done_testing;
+
commit 1233ed1a6ac9a85b4e66d71f3c52498cbb350aed
Author: gibus <gibus at easter-eggs.com>
Date: Sun Oct 21 10:28:08 2018 +0200
Add searching for customfields attached to a catalog/class/queue
Add documentation for searching for customfields attached to a catalog/class/queue
Based on public github PR #22
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 7b17587..4cc1514 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -531,6 +531,18 @@ Below are some examples using the endpoints above.
POST /customfield
create a customfield; provide JSON content
+ GET /catalog/:id/customfields?query=<JSON>
+ POST /catalog/:id/customfields
+ search for custom fields attached to a catalog using L</JSON searches> syntax
+
+ GET /class/:id/customfields?query=<JSON>
+ POST /class/:id/customfields
+ search for custom fields attached to a class using L</JSON searches> syntax
+
+ GET /queue/:id/customfields?query=<JSON>
+ POST /queue/:id/customfields
+ search for custom fields attached to a queue using L</JSON searches> syntax
+
GET /customfield/:id
retrieve a custom field, with values if type is Select
diff --git a/lib/RT/Extension/REST2/Resource/CustomFields.pm b/lib/RT/Extension/REST2/Resource/CustomFields.pm
index e227dca..a041010 100644
--- a/lib/RT/Extension/REST2/Resource/CustomFields.pm
+++ b/lib/RT/Extension/REST2/Resource/CustomFields.pm
@@ -8,13 +8,40 @@ use namespace::autoclean;
extends 'RT::Extension::REST2::Resource::Collection';
with 'RT::Extension::REST2::Resource::Collection::QueryByJSON';
+has 'object_applied_to' => (
+ is => 'ro',
+ required => 0,
+);
+
sub dispatch_rules {
Path::Dispatcher::Rule::Regex->new(
regex => qr{^/customfields/?$},
block => sub { { collection_class => 'RT::CustomFields' } },
),
+ Path::Dispatcher::Rule::Regex->new(
+ regex => qr{^/(catalog|class|queue)/(\d+)/customfields/?$},
+ block => sub {
+ my ($match, $req) = @_;
+ my $object_type = 'RT::'. ucfirst($match->pos(1));
+ my $object_id = $match->pos(2);
+ my $object_applied_to = $object_type->new($req->env->{"rt.current_user"});
+ $object_applied_to->Load($object_id);
+ return {object_applied_to => $object_applied_to, collection_class => 'RT::CustomFields'};
+ },
+ ),
}
+after 'limit_collection' => sub {
+ my $self = shift;
+ my $collection = $self->collection;
+ my $object = $self->object_applied_to;
+ if ($object && $object->id) {
+ $collection->Limit(ENTRYAGGREGATOR => "AND", FIELD => 'LookupType', OPERATOR => 'STARTSWITH', VALUE => ref($object));
+ $collection->LimitToGlobalOrObjectId($object->id);
+ }
+ return 1;
+};
+
__PACKAGE__->meta->make_immutable;
1;
diff --git a/t/customfields.t b/t/customfields.t
new file mode 100644
index 0000000..766e153
--- /dev/null
+++ b/t/customfields.t
@@ -0,0 +1,221 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use RT::Extension::REST2::Test tests => undef;
+
+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;
+
+my $freeform_cf = RT::CustomField->new(RT->SystemUser);
+$freeform_cf->Create(Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1, Queue => 'General');
+my $freeform_cf_id = $freeform_cf->id;
+
+my $select_cf = RT::CustomField->new(RT->SystemUser);
+$select_cf->Create(Name => 'Select CF', Type => 'Select', MaxValues => 1, Queue => 'General');
+$select_cf->AddValue(Name => 'First Value', SortOder => 0);
+$select_cf->AddValue(Name => 'Second Value', SortOrder => 1);
+$select_cf->AddValue(Name => 'Third Value', SortOrder => 2);
+my $select_cf_id = $select_cf->id;
+my $select_cf_values = $select_cf->Values->ItemsArrayRef;
+
+my $basedon_cf = RT::CustomField->new(RT->SystemUser);
+$basedon_cf->Create(Name => 'SubSelect CF', Type => 'Select', MaxValues => 1, Queue => 'General', BasedOn => $select_cf->id);
+$basedon_cf->AddValue(Name => 'With First Value', Category => $select_cf_values->[0]->Name, SortOder => 0);
+$basedon_cf->AddValue(Name => 'With No Value', SortOder => 0);
+my $basedon_cf_id = $basedon_cf->id;
+my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
+
+# Right test - search all tickets customfields without SeeCustomField
+{
+ my $res = $mech->post_json("$rest_base_path/customfields",
+ [{field => 'LookupType', value => 'RT::Queue-RT::Ticket'}],
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ is($content->{total}, 3);
+ is($content->{count}, 0);
+ is_deeply($content->{items}, []);
+}
+
+# search all tickets customfields
+{
+ $user->PrincipalObj->GrantRight( Right => 'SeeCustomField' );
+
+ my $res = $mech->post_json("$rest_base_path/customfields",
+ [{field => 'LookupType', value => 'RT::Queue-RT::Ticket'}],
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+
+ my $content = $mech->json_response;
+ is($content->{total}, 3);
+ is($content->{count}, 3);
+ my $items = $content->{items};
+ is(scalar(@$items), 3);
+
+ is($items->[0]->{type}, 'customfield');
+ is($items->[0]->{id}, $freeform_cf->id);
+ like($items->[0]->{_url}, qr{$rest_base_path/customfield/$freeform_cf_id$});
+
+ is($items->[1]->{type}, 'customfield');
+ is($items->[1]->{id}, $select_cf->id);
+ like($items->[1]->{_url}, qr{$rest_base_path/customfield/$select_cf_id$});
+
+ is($items->[2]->{type}, 'customfield');
+ is($items->[2]->{id}, $basedon_cf->id);
+ like($items->[2]->{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+}
+
+# Freeform CustomField display
+{
+ my $res = $mech->get("$rest_base_path/customfield/$freeform_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+ my $content = $mech->json_response;
+ is($content->{id}, $freeform_cf_id);
+ is($content->{Name}, $freeform_cf->Name);
+ is($content->{Description}, '');
+ is($content->{LookupType}, 'RT::Queue-RT::Ticket');
+ is($content->{Type}, 'Freeform');
+ is($content->{MaxValues}, 1);
+ is($content->{Disabled}, 0);
+
+ my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
+ push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
+ ok(exists $content->{$_}, "got $_") for @fields;
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 1);
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $freeform_cf_id);
+ is($links->[0]{type}, 'customfield');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$freeform_cf_id$});
+}
+
+# Select CustomField display
+{
+ my $res = $mech->get("$rest_base_path/customfield/$select_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+ my $content = $mech->json_response;
+ is($content->{id}, $select_cf_id);
+ is($content->{Name}, $select_cf->Name);
+ is($content->{Description}, '');
+ is($content->{LookupType}, 'RT::Queue-RT::Ticket');
+ is($content->{Type}, 'Select');
+ is($content->{MaxValues}, 1);
+ is($content->{Disabled}, 0);
+
+ my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
+ push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
+ ok(exists $content->{$_}, "got $_") for @fields;
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 1);
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $select_cf_id);
+ is($links->[0]{type}, 'customfield');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$select_cf_id$});
+
+ my $values = $content->{Values};
+ is_deeply($values, ['First Value', 'Second Value', 'Third Value']);
+}
+
+# BasedOn CustomField display
+{
+ my $res = $mech->get("$rest_base_path/customfield/$basedon_cf_id",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+ my $content = $mech->json_response;
+ is($content->{id}, $basedon_cf_id);
+ is($content->{Name}, $basedon_cf->Name);
+ is($content->{Description}, '');
+ is($content->{LookupType}, 'RT::Queue-RT::Ticket');
+ is($content->{Type}, 'Select');
+ is($content->{MaxValues}, 1);
+ is($content->{Disabled}, 0);
+
+ my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
+ push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
+ ok(exists $content->{$_}, "got $_") for @fields;
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 1);
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $basedon_cf_id);
+ is($links->[0]{type}, 'customfield');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+
+ my $values = $content->{Values};
+ is_deeply($values, ['With First Value', 'With No Value']);
+}
+
+# BasedOn CustomField display with category filter
+{
+ my $res = $mech->get("$rest_base_path/customfield/$basedon_cf_id?category=First%20Value",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+ my $content = $mech->json_response;
+ is($content->{id}, $basedon_cf_id);
+ is($content->{Name}, $basedon_cf->Name);
+ is($content->{Description}, '');
+ is($content->{LookupType}, 'RT::Queue-RT::Ticket');
+ is($content->{Type}, 'Select');
+ is($content->{MaxValues}, 1);
+ is($content->{Disabled}, 0);
+
+ my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
+ push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
+ ok(exists $content->{$_}, "got $_") for @fields;
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 1);
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $basedon_cf_id);
+ is($links->[0]{type}, 'customfield');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+
+ my $values = $content->{Values};
+ is_deeply($values, ['With First Value']);
+}
+
+# BasedOn CustomField display with null category filter
+{
+ my $res = $mech->get("$rest_base_path/customfield/$basedon_cf_id?category=",
+ 'Authorization' => $auth,
+ );
+ is($res->code, 200);
+ my $content = $mech->json_response;
+ is($content->{id}, $basedon_cf_id);
+ is($content->{Name}, $basedon_cf->Name);
+ is($content->{Description}, '');
+ is($content->{LookupType}, 'RT::Queue-RT::Ticket');
+ is($content->{Type}, 'Select');
+ is($content->{MaxValues}, 1);
+ is($content->{Disabled}, 0);
+
+ my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
+ push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
+ ok(exists $content->{$_}, "got $_") for @fields;
+
+ my $links = $content->{_hyperlinks};
+ is(scalar @$links, 1);
+ is($links->[0]{ref}, 'self');
+ is($links->[0]{id}, $basedon_cf_id);
+ is($links->[0]{type}, 'customfield');
+ like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+
+ my $values = $content->{Values};
+ is_deeply($values, ['With No Value']);
+}
+
+done_testing;
+
commit f3d73ebc2b8831ea2019214053dca36c1cf7cb78
Author: gibus <gibus at easter-eggs.com>
Date: Sun Oct 21 10:32:07 2018 +0200
Add tests for searching for customfields attached to a catalog/class/queue
Based on public github PR #22
diff --git a/t/customfields.t b/t/customfields.t
index 766e153..6590ad2 100644
--- a/t/customfields.t
+++ b/t/customfields.t
@@ -7,44 +7,31 @@ 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;
+$user->PrincipalObj->GrantRight( Right => 'SeeCustomField' );
-my $freeform_cf = RT::CustomField->new(RT->SystemUser);
-$freeform_cf->Create(Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1, Queue => 'General');
-my $freeform_cf_id = $freeform_cf->id;
+my $queue = RT::Queue->new(RT->SystemUser);
+$queue->Load('General');
+my $queue_id = $queue->id;
-my $select_cf = RT::CustomField->new(RT->SystemUser);
-$select_cf->Create(Name => 'Select CF', Type => 'Select', MaxValues => 1, Queue => 'General');
-$select_cf->AddValue(Name => 'First Value', SortOder => 0);
-$select_cf->AddValue(Name => 'Second Value', SortOrder => 1);
-$select_cf->AddValue(Name => 'Third Value', SortOrder => 2);
-my $select_cf_id = $select_cf->id;
-my $select_cf_values = $select_cf->Values->ItemsArrayRef;
+my $attached_single_cf = RT::CustomField->new(RT->SystemUser);
+$attached_single_cf->Create(LookupType => 'RT::Queue-RT::Ticket', Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1, Queue => 'General');
+my $attached_single_cf_id = $attached_single_cf->id;
-my $basedon_cf = RT::CustomField->new(RT->SystemUser);
-$basedon_cf->Create(Name => 'SubSelect CF', Type => 'Select', MaxValues => 1, Queue => 'General', BasedOn => $select_cf->id);
-$basedon_cf->AddValue(Name => 'With First Value', Category => $select_cf_values->[0]->Name, SortOder => 0);
-$basedon_cf->AddValue(Name => 'With No Value', SortOder => 0);
-my $basedon_cf_id = $basedon_cf->id;
-my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
+my $attached_multiple_cf = RT::CustomField->new(RT->SystemUser);
+$attached_multiple_cf->Create(LookupType => 'RT::Queue-RT::Ticket', Name => 'Freeform CF', Type => 'Freeform', MaxValues => 0, Queue => 'General');
+my $attached_multiple_cf_id = $attached_multiple_cf->id;
-# Right test - search all tickets customfields without SeeCustomField
-{
- my $res = $mech->post_json("$rest_base_path/customfields",
- [{field => 'LookupType', value => 'RT::Queue-RT::Ticket'}],
- 'Authorization' => $auth,
- );
- is($res->code, 200);
+my $detached_cf = RT::CustomField->new(RT->SystemUser);
+$detached_cf->Create(LookupType => 'RT::Queue-RT::Ticket', Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1);
+my $detached_cf_id = $detached_cf->id;
- my $content = $mech->json_response;
- is($content->{total}, 3);
- is($content->{count}, 0);
- is_deeply($content->{items}, []);
-}
+my $queue_cf = RT::CustomField->new(RT->SystemUser);
+$queue_cf->Create(LookupType => 'RT::Queue', Name => 'Freeform CF', Type => 'Freeform', MaxValues => 1);
+$queue_cf->AddToObject($queue);
+my $queue_cf_id = $queue_cf->id;
-# search all tickets customfields
+# All tickets customfields
{
- $user->PrincipalObj->GrantRight( Right => 'SeeCustomField' );
-
my $res = $mech->post_json("$rest_base_path/customfields",
[{field => 'LookupType', value => 'RT::Queue-RT::Ticket'}],
'Authorization' => $auth,
@@ -54,167 +41,45 @@ my $basedon_cf_values = $basedon_cf->Values->ItemsArrayRef;
my $content = $mech->json_response;
is($content->{total}, 3);
is($content->{count}, 3);
- my $items = $content->{items};
- is(scalar(@$items), 3);
-
- is($items->[0]->{type}, 'customfield');
- is($items->[0]->{id}, $freeform_cf->id);
- like($items->[0]->{_url}, qr{$rest_base_path/customfield/$freeform_cf_id$});
-
- is($items->[1]->{type}, 'customfield');
- is($items->[1]->{id}, $select_cf->id);
- like($items->[1]->{_url}, qr{$rest_base_path/customfield/$select_cf_id$});
-
- is($items->[2]->{type}, 'customfield');
- is($items->[2]->{id}, $basedon_cf->id);
- like($items->[2]->{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
+ is(scalar @{$content->{items}}, 3);
+ my @ids = sort map {$_->{id}} @{$content->{items}};
+ is_deeply(\@ids, [$attached_single_cf_id, $attached_multiple_cf_id, $detached_cf_id]);
}
-# Freeform CustomField display
+# All tickets single customfields attached to queue 'General'
{
- my $res = $mech->get("$rest_base_path/customfield/$freeform_cf_id",
+ my $res = $mech->post_json("$rest_base_path/queue/$queue_id/customfields",
+ [
+ {field => 'LookupType', value => 'RT::Queue-RT::Ticket'},
+ {field => 'MaxValues', value => 1},
+ ],
'Authorization' => $auth,
);
is($res->code, 200);
- my $content = $mech->json_response;
- is($content->{id}, $freeform_cf_id);
- is($content->{Name}, $freeform_cf->Name);
- is($content->{Description}, '');
- is($content->{LookupType}, 'RT::Queue-RT::Ticket');
- is($content->{Type}, 'Freeform');
- is($content->{MaxValues}, 1);
- is($content->{Disabled}, 0);
-
- my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
- push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
- ok(exists $content->{$_}, "got $_") for @fields;
-
- my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
- is($links->[0]{ref}, 'self');
- is($links->[0]{id}, $freeform_cf_id);
- is($links->[0]{type}, 'customfield');
- like($links->[0]{_url}, qr{$rest_base_path/customfield/$freeform_cf_id$});
-}
-# Select CustomField display
-{
- my $res = $mech->get("$rest_base_path/customfield/$select_cf_id",
- 'Authorization' => $auth,
- );
- is($res->code, 200);
my $content = $mech->json_response;
- is($content->{id}, $select_cf_id);
- is($content->{Name}, $select_cf->Name);
- is($content->{Description}, '');
- is($content->{LookupType}, 'RT::Queue-RT::Ticket');
- is($content->{Type}, 'Select');
- is($content->{MaxValues}, 1);
- is($content->{Disabled}, 0);
-
- my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
- push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
- ok(exists $content->{$_}, "got $_") for @fields;
-
- my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
- is($links->[0]{ref}, 'self');
- is($links->[0]{id}, $select_cf_id);
- is($links->[0]{type}, 'customfield');
- like($links->[0]{_url}, qr{$rest_base_path/customfield/$select_cf_id$});
-
- my $values = $content->{Values};
- is_deeply($values, ['First Value', 'Second Value', 'Third Value']);
+ is($content->{total}, 1);
+ is($content->{count}, 1);
+ is(scalar @{$content->{items}}, 1);
+ is($content->{items}->[0]->{id}, $attached_single_cf_id);
}
-# BasedOn CustomField display
+# All single customfields attached to queue 'General'
{
- my $res = $mech->get("$rest_base_path/customfield/$basedon_cf_id",
+ my $res = $mech->post_json("$rest_base_path/queue/$queue_id/customfields",
+ [
+ {field => 'MaxValues', value => 1},
+ ],
'Authorization' => $auth,
);
is($res->code, 200);
- my $content = $mech->json_response;
- is($content->{id}, $basedon_cf_id);
- is($content->{Name}, $basedon_cf->Name);
- is($content->{Description}, '');
- is($content->{LookupType}, 'RT::Queue-RT::Ticket');
- is($content->{Type}, 'Select');
- is($content->{MaxValues}, 1);
- is($content->{Disabled}, 0);
-
- my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
- push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
- ok(exists $content->{$_}, "got $_") for @fields;
-
- my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
- is($links->[0]{ref}, 'self');
- is($links->[0]{id}, $basedon_cf_id);
- is($links->[0]{type}, 'customfield');
- like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
-
- my $values = $content->{Values};
- is_deeply($values, ['With First Value', 'With No Value']);
-}
-
-# BasedOn CustomField display with category filter
-{
- my $res = $mech->get("$rest_base_path/customfield/$basedon_cf_id?category=First%20Value",
- 'Authorization' => $auth,
- );
- is($res->code, 200);
- my $content = $mech->json_response;
- is($content->{id}, $basedon_cf_id);
- is($content->{Name}, $basedon_cf->Name);
- is($content->{Description}, '');
- is($content->{LookupType}, 'RT::Queue-RT::Ticket');
- is($content->{Type}, 'Select');
- is($content->{MaxValues}, 1);
- is($content->{Disabled}, 0);
- my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
- push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
- ok(exists $content->{$_}, "got $_") for @fields;
-
- my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
- is($links->[0]{ref}, 'self');
- is($links->[0]{id}, $basedon_cf_id);
- is($links->[0]{type}, 'customfield');
- like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
-
- my $values = $content->{Values};
- is_deeply($values, ['With First Value']);
-}
-
-# BasedOn CustomField display with null category filter
-{
- my $res = $mech->get("$rest_base_path/customfield/$basedon_cf_id?category=",
- 'Authorization' => $auth,
- );
- is($res->code, 200);
my $content = $mech->json_response;
- is($content->{id}, $basedon_cf_id);
- is($content->{Name}, $basedon_cf->Name);
- is($content->{Description}, '');
- is($content->{LookupType}, 'RT::Queue-RT::Ticket');
- is($content->{Type}, 'Select');
- is($content->{MaxValues}, 1);
- is($content->{Disabled}, 0);
-
- my @fields = qw(SortOrder Pattern Created Creator LastUpdated LastUpdatedBy);
- push @fields, qw(UniqueValues EntryHint) if RT::Handle::cmp_version($RT::VERSION, '4.4.0') >= 0;
- ok(exists $content->{$_}, "got $_") for @fields;
-
- my $links = $content->{_hyperlinks};
- is(scalar @$links, 1);
- is($links->[0]{ref}, 'self');
- is($links->[0]{id}, $basedon_cf_id);
- is($links->[0]{type}, 'customfield');
- like($links->[0]{_url}, qr{$rest_base_path/customfield/$basedon_cf_id$});
-
- my $values = $content->{Values};
- is_deeply($values, ['With No Value']);
+ is($content->{total}, 2);
+ is($content->{count}, 2);
+ is(scalar @{$content->{items}}, 2);
+ my @ids = sort map {$_->{id}} @{$content->{items}};
+ is_deeply(\@ids, [$attached_single_cf_id, $queue_cf_id]);
}
done_testing;
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list