[Bps-public-commit] rt-extension-rest2 04/07: Add /searches/ and /search/ endpoits for saved searches

sunnavy sunnavy at bestpractical.com
Wed Jul 14 10:05:51 EDT 2021


This is an automated email from the git hooks/post-receive script.

sunnavy pushed a commit to branch saved-searches
in repository rt-extension-rest2.

commit 39175cafffaa69d2798ea87cd56be9073b73e3e9
Author: sunnavy <sunnavy at bestpractical.com>
AuthorDate: Thu May 27 04:48:20 2021 +0800

    Add /searches/ and /search/ endpoits for saved searches
---
 lib/RT/Extension/REST2/Resource/Search.pm   | 110 ++++++++++++++++++++++++++++
 lib/RT/Extension/REST2/Resource/Searches.pm |  93 +++++++++++++++++++++++
 2 files changed, 203 insertions(+)

diff --git a/lib/RT/Extension/REST2/Resource/Search.pm b/lib/RT/Extension/REST2/Resource/Search.pm
new file mode 100644
index 0000000..56545f1
--- /dev/null
+++ b/lib/RT/Extension/REST2/Resource/Search.pm
@@ -0,0 +1,110 @@
+package RT::Extension::REST2::Resource::Search;
+use strict;
+use warnings;
+
+use Moose;
+use namespace::autoclean;
+
+extends 'RT::Extension::REST2::Resource::Record';
+with 'RT::Extension::REST2::Resource::Record::Readable',
+    'RT::Extension::REST2::Resource::Record::Hypermedia' =>
+    { -alias => { _self_link => '_default_self_link', hypermedia_links => '_default_hypermedia_links' } };
+
+sub dispatch_rules {
+    Path::Dispatcher::Rule::Regex->new(
+        regex => qr{^/search/?$},
+        block => sub { { record_class => 'RT::Attribute' } },
+        ),
+        Path::Dispatcher::Rule::Regex->new(
+            regex => qr{^/search/(.+)/?$},
+            block => sub {
+                my ($match, $req) = @_;
+                my $desc = $match->pos(1);
+                my $record = _load_search($req, $desc);
+
+                return { record_class => 'RT::Attribute', record_id => $record ? $record->Id : 0 };
+            },
+        );
+}
+
+sub _self_link {
+    my $self   = shift;
+    my $result = $self->_default_self_link(@_);
+
+    $result->{type} = 'search';
+    $result->{_url} =~ s!/attribute/!/search/!;
+    return $result;
+}
+
+sub hypermedia_links {
+    my $self = shift;
+    my $links = $self->_default_hypermedia_links;
+    my $record = $self->record;
+    if ( my $content = $record->Content ) {
+        if ( ( $content->{SearchType} || 'Ticket' ) eq 'Ticket' ) {
+            my $id = $record->Id;
+            push @$links,
+                {   _url => RT::Extension::REST2->base_uri . "/tickets?search=$id",
+                    type => 'results',
+                    ref  => 'tickets',
+                };
+        }
+    }
+    return $links;
+}
+
+sub base_uri { join '/', RT::Extension::REST2->base_uri, 'search' }
+
+sub resource_exists {
+    my $self   = shift;
+    my $record = $self->record;
+    return $record->Id && $record->Name =~ /^(?:SavedSearch$|Search -)/;
+}
+
+sub forbidden {
+    my $self = shift;
+    return 0 unless $self->resource_exists;
+    my $search = RT::SavedSearch->new( $self->current_user );
+    return $search->LoadById( $self->record->Id ) ? 0 : 1;
+}
+
+sub _load_search {
+    my $req = shift;
+    my $id  = shift;
+
+    if ( $id =~ /\D/ ) {
+
+        my $attrs = RT::Attributes->new( $req->env->{"rt.current_user"} );
+
+        $attrs->Limit( FIELD => 'Name',        VALUE => 'SavedSearch' );
+        $attrs->Limit( FIELD => 'Name',        VALUE => 'Search -', OPERATOR => 'STARTSWITH' );
+        $attrs->Limit( FIELD => 'Description', VALUE => $id );
+
+        my @searches;
+        while ( my $attr = $attrs->Next ) {
+            my $search = RT::SavedSearch->new( $req->env->{"rt.current_user"} );
+            if ( $search->LoadById( $attr->Id ) ) {
+                push @searches, $search;
+            }
+        }
+
+        my $record_id;
+        if (@searches) {
+            if ( @searches > 1 ) {
+                RT->Logger->warning("Found multiple searches with description $id");
+            }
+            return $searches[0];
+        }
+    }
+    else {
+        my $search = RT::SavedSearch->new( $req->env->{"rt.current_user"} );
+        if ( $search->LoadById($id) ) {
+            return $search;
+        }
+    }
+    return;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/lib/RT/Extension/REST2/Resource/Searches.pm b/lib/RT/Extension/REST2/Resource/Searches.pm
new file mode 100644
index 0000000..3e0b7a1
--- /dev/null
+++ b/lib/RT/Extension/REST2/Resource/Searches.pm
@@ -0,0 +1,93 @@
+package RT::Extension::REST2::Resource::Searches;
+use strict;
+use warnings;
+
+use Moose;
+use namespace::autoclean;
+
+extends 'RT::Extension::REST2::Resource::Collection';
+with 'RT::Extension::REST2::Resource::Collection::ProcessPOSTasGET',
+    'RT::Extension::REST2::Resource::Collection::QueryByJSON';
+
+sub dispatch_rules {
+    Path::Dispatcher::Rule::Regex->new(
+        regex => qr{^/searches/?$},
+        block => sub { { collection_class => 'RT::Attributes' } },
+    )
+}
+
+use Encode qw( decode_utf8 );
+use RT::Extension::REST2::Util qw( error_as_json );
+use RT::Search::Simple;
+
+sub allowed_methods {
+    [ 'GET', 'HEAD', 'POST' ]
+}
+
+sub limit_collection {
+    my $self = shift;
+    my @objects = RT::SavedSearch->new($self->current_user)->ObjectsForLoading;
+    if ( $self->current_user->HasRight( Object => $RT::System, Right => 'ShowSavedSearches' ) ) {
+        push @objects, RT::System->new( $self->current_user );
+    }
+
+    my $query       = $self->query;
+    my @fields      = $self->searchable_fields;
+    my %searchable  = map {; $_ => 1 } @fields;
+
+    my @ids;
+    my @attrs;
+    for my $object (@objects) {
+        my $attrs = $object->Attributes;
+        $attrs->Limit( FIELD => 'Name', VALUE => 'SavedSearch' );
+        push @attrs, $attrs;
+    }
+
+    # Default system searches
+    my $attrs = RT::System->new( $self->current_user )->Attributes;
+    $attrs->Limit( FIELD => 'Name', VALUE => 'Search -', OPERATOR => 'STARTSWITH' );
+    push @attrs, $attrs;
+
+    for my $attrs (@attrs) {
+        for my $limit (@$query) {
+            next
+                unless $limit->{field}
+                and $searchable{ $limit->{field} }
+                and defined $limit->{value};
+
+            $attrs->Limit(
+                FIELD => $limit->{field},
+                VALUE => $limit->{value},
+                (   $limit->{operator} ? ( OPERATOR => $limit->{operator} )
+                    : ()
+                ),
+                CASESENSITIVE => ( $limit->{case_sensitive} || 0 ),
+                (   $limit->{entry_aggregator} ? ( ENTRYAGGREGATOR => $limit->{entry_aggregator} )
+                    : ()
+                ),
+            );
+        }
+        push @ids, map { $_->Id } @{ $attrs->ItemsArrayRef };
+    }
+
+    while ( @ids > 1000 ) {
+        my @batch = splice( @ids, 0, 1000 );
+        $self->Limit( FIELD => 'id', VALUE => \@ids, OPERATOR => 'IN' );
+    }
+    $self->collection->Limit( FIELD => 'id', VALUE => \@ids, OPERATOR => 'IN' );
+
+    return 1;
+}
+
+sub serialize_record {
+    my $self   = shift;
+    my $record = shift;
+    my $result = $self->SUPER::serialize_record($record);
+    $result->{type} = 'search';
+    $result->{_url} =~ s!/attribute/!/search/!;
+    return $result;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;

-- 
To stop receiving notification emails like this one, please contact
sysadmin at bestpractical.com.


More information about the Bps-public-commit mailing list