Introduced KalamarErrors plugin
Change-Id: I72fc22a702e41af7beec9d1cbdf3874e65bd2bb4
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index 8a3274a..31fcb47 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -144,7 +144,8 @@
'Search', # Abstract Search framework
'TagHelpers::MailToChiffre', # Obfuscate email addresses
'KalamarHelpers', # Specific Helpers for Kalamar
- 'KalamarUser', # Specific Helpers for Kalamar
+ 'KalamarErrors', # Specific Errors for Kalamar
+ 'KalamarUser', # Specific Helpers for Kalamar Users
'ClientIP', # Get client IP from X-Forwarded-For
'ClosedRedirect', # Redirect with OpenRedirect protection
'TagHelpers::ContentBlock', # Flexible content blocks
diff --git a/lib/Kalamar/Controller/Search2.pm b/lib/Kalamar/Controller/Search2.pm
index 7bb0b99..944b7a4 100644
--- a/lib/Kalamar/Controller/Search2.pm
+++ b/lib/Kalamar/Controller/Search2.pm
@@ -5,83 +5,343 @@
use Mojo::ByteStream 'b';
use POSIX 'ceil';
-# This should be implemented as a helper
-has api => '/api/';
-
has no_cache => 0;
has items_per_page => 25;
-
# TODO:
# Support server timing API
-# Catch connection errors
-sub _catch_http_errors {
- my $tx = shift;
- my $err = $tx->error;
+# Query endpoint
+sub query {
+ my $c = shift;
- if ($err) {
- # print $err->code, "\n";
- return Mojo::Promise->new->reject([
- [$err->{code}, $err->{message}]
- ]);
- };
- return $tx->result;
-};
+ # Validate user input
+ my $v = $c->validation;
+ # In case the user is not known, it is assumed, the user is not logged in
+ my $user = $c->stash('user') // 'not_logged_in';
-# Catch koral errors
-sub _catch_koral_errors {
- my $json = shift;
+ $v->optional('q', 'trim');
+ $v->optional('ql')->in(qw/poliqarp cosmas2 annis cql fcsql/);
+ $v->optional('collection', 'trim'); # Legacy
+ $v->optional('cq', 'trim');
+ # $v->optional('action'); # action 'inspect' is no longer valid
+ # $v->optional('snippet');
+ $v->optional('cutoff')->in(qw/true false/);
+ $v->optional('count')->num(1, undef);
+ $v->optional('p', 'trim')->num(1, undef); # Start page
+ $v->optional('o', 'trim')->num(1, undef); # Offset
+ $v->optional('context');
- # Get errors
- my $err = $json->{errors};
+ # Get query
+ my $query = $v->param('q');
- # Create error message
- if ($err) {
- return Mojo::Promise->new->reject($err);
+ # TODO:
+ # Check for validation errors!
+
+ # No query
+ unless ($query) {
+ return $c->render($c->loc('Template_intro', 'intro'));
};
- # TODO: What does status mean?
- if ($json->{status}) {
- return Mojo::Promise->new->reject([
- [undef, 'Middleware error ' . $json->{'status'}]
- ]);
+ my %query = ();
+ $query{q} = $v->param('q');
+ $query{ql} = $v->param('ql') // 'poliqarp';
+ $query{p} = $v->param('p') // 1; # Start page
+ $query{count} = $v->param('count') // $c->items_per_page;
+ $query{cq} = $v->param('cq') // $v->param('collection');
+ $query{cutoff} = $v->param('cutoff');
+
+ # Before: 'base/s:p'/'paragraph'
+ $query{context} = $v->param('context') // '40-t,40-t';
+
+ my $items_per_page = $c->items_per_page;
+
+ # Set count
+ if ($query{count} && $query{count} <= $c->items_per_page ) {
+ $items_per_page = delete $query{count};
};
- return $json;
-};
+ $c->stash(items_per_page => $items_per_page);
+
+ # Set offset
+ # From Mojolicious::Plugin::Search::Index
+ $query{o} = $v->param('o') || ((($query{p} // 1) - 1) * ($items_per_page || 1));
-# Notify the user in case of warnings
-sub _notify_on_warnings {
- my ($self, $warnings) = @_;
+ # already set by stash - or use plugin param
+ # else {
+ # $items_per_page = $c->stash('search.count') // $plugin->items_per_page
+ # };
- # TODO: Check for ref!
- foreach my $w (@$warnings) {
- $self->notify(
- warn =>
- ($w->[0] ? $w->[0] . ': ' : '') .
- $w->[1]
+ # Set start page based on param
+ #if ($query{p}) {
+ # $index->start_page(delete $param{start_page});
+ #}
+ ## already set by stash
+ #elsif ($c->stash('search.start_page')) {
+ # $index->start_page($c->stash('search.start_page'));
+ #};
+
+
+ # Create remote request URL
+ my $url = Mojo::URL->new($c->korap->api);
+ $url->path('search');
+ $url->query(\%query);
+
+ # Check if total results is cached
+ my $total_results = -1;
+ unless ($c->no_cache) {
+
+ # Get total results value
+ $total_results = $c->chi->get('total-' . $user . '-' . $url->to_string);
+
+ # Set stash if cache exists
+ $c->stash(total_results => $total_results) if $total_results;
+ $c->app->log->debug('Get total result from cache');
+
+ # Set cutoff unless already set
+ $url->query({cutoff => 'true'});
+ };
+
+ # Check if the request is cached
+ my $url_string = $url->to_string;
+
+ # Set api request for debugging
+ $c->stash(api_request => $url_string);
+
+ # Debugging
+ $c->app->log->debug("Search for $url_string");
+
+ # Check for cache
+ my $json = $c->chi->get("matches-$user-$url_string");
+
+ # Initialize promise object
+ my $promise;
+
+ # Result is cached
+ if ($json) {
+ $json->{cached} = 'true';
+
+ # The promise is already satisfied by the cache
+ $promise = Mojo::Promise->new->resolve($json)->then(
+ sub {
+ my $json = shift;
+ $c->notify_on_warnings($json);
+ return $json;
+ }
+ );
+ }
+
+ # Retrieve from URL
+ else {
+
+ # Wrap a user agent method with a promise
+ $promise = $c->user->auth_request_p(get => $url)->then(
+ sub {
+
+ # Catch errors and warnings
+ return $c->catch_errors_and_warnings(shift)
+ }
+ );
+ };
+
+ # Wait for rendering
+ $c->render_later;
+
+ # Choose the snippet based on the parameter
+ # scalar $v->param('snippet') ? 'snippet' : 'search2';
+ my $template = 'search2';
+ $c->stash(template => $template);
+
+ # Process response
+ $promise->then(
+ sub {
+ my $json = shift;
+
+ #######################
+ # Cache total results #
+ #######################
+ # The stash is set in case the total results value is from the cache,
+ # so in that case, it does not need to be cached again
+ my $total_results = $c->stash('total_results');
+ if (!$total_results) {
+
+ # There are results to remember
+ if ($json->{meta}->{totalResults} >= 0) {
+
+ # Remove cutoff requirement again
+ $url->query([cutoff => 'true']);
+
+ $total_results = $json->{meta}->{totalResults};
+ $c->stash(total_results => $total_results);
+
+ # Set cache
+ $c->chi->set(
+ 'total-' . $user . '-' . $url->to_string => $total_results
+ );
+ };
+ }
+ else {
+ $c->stash(total_results => -1);
+ }
+
+ $c->stash(total_pages => 0);
+
+ # Set total pages
+ # From Mojolicious::Plugin::Search::Index
+ if ($total_results > 0) {
+ $c->stash(
+ total_pages => ceil($total_results / ($c->stash('items_per_page') || 1))
+ );
+ };
+
+ # Cache result
+ $c->chi->set('matches-' . $user . '-' . $url_string => $json);
+
+ # Process match results
+ $c->_process_query_response($json);
+
+ # Render result
+ return $c->render(
+ q => $query,
+ ql => $query{ql},
+ start_page => $query{p},
);
- };
+
+ }
+
+ # Deal with errors
+ )->catch(
+ sub {
+
+ # $c->_notify_on_errors(shift);
+ return $c->render(
+ results => c(),
+ q => $query,
+ ql => $query{ql},
+ start_page => 1
+ );
+ }
+ )
+
+ # Start IOLoop
+ ->wait;
+
+ return 1;
};
-sub _notify_on_errors {
- my ($self, $errors) = @_;
- foreach my $e (@$errors) {
- $self->notify(
- error =>
- ($e->[0] ? $e->[0] . ': ' : '') .
- ($e->[1] || 'Unknown')
- );
+
+# Match info endpoint
+sub match_info {
+ my $c = shift;
+
+ # Validate user input
+ my $v = $c->validation;
+ $v->optional('foundry');
+ $v->optional('layer');
+ $v->optional('spans')->in(qw/true false/);
+
+ # Old API foundry/layer usage
+ my $foundry = '*';
+ my %query = (foundry => '*');
+ if ($v->param('foundry')) {
+ $query{foundry} = $v->param('foundry');
+ $query{layer} = $v->param('layer') if $v->param('layer');
+ $query{spans} = $v->param('spans') if $v->param('spans');
};
+
+ # Create new request API
+ my $url = Mojo::URL->new($c->korap->api);
+
+ # Use stash information to create url path
+ $url->path(
+ join('/', (
+ 'corpus',
+ $c->stash('corpus_id'),
+ $c->stash('doc_id'),
+ $c->stash('text_id'),
+ $c->stash('match_id'),
+ 'matchInfo'
+ ))
+ );
+
+ # Set query parameters
+ $url->query(\%query);
+
+ $c->render_later;
+
+ # TODO: Add caching!
+ $c->user->auth_request_p(get => $url)->then(
+ sub {
+ return $c->catch_errors_and_warnings(shift);
+ }
+ )
+ ->then(
+ sub {
+ my $json = shift;
+
+ # Process results
+ $json = _map_match($json);
+ $c->stash(results => $json);
+
+ return $c->render(
+ json => $c->notifications(json => $json),
+ status => 200
+ );
+
+ return $json;
+ }
+ )
+ # Deal with errors
+ ->catch(
+ sub {
+ return $c->render(
+ json => $c->notifications('json')
+ )
+ }
+ )
+
+ # Start IOLoop
+ ->wait;
+
+ return 1;
+};
+
+
+# Cleanup array of matches
+sub _map_matches {
+ return c() unless $_[0];
+ c(map { _map_match($_) } @{ shift() });
+};
+
+
+# Cleanup single match
+sub _map_match {
+ my $match = shift or return;
+
+ # Legacy match id
+ if ($match->{matchID}) {
+ $match->{matchID} =~ s/^match\-(?:[^!]+!|[^_]+_)[^\.]+?\.[^-]+?-// or
+ $match->{matchID} =~ s!^match\-(?:[^\/]+\/){2}[^-]+?-!!;
+ };
+
+ return unless $match->{textSigle};
+
+ # Set IDs based on the sigle
+ (
+ $match->{corpusID},
+ $match->{docID},
+ $match->{textID}
+ ) = ($match->{textSigle} =~ /^([^_]+?)_+([^\.]+?)\.(.+?)$/);
+
+ return $match;
};
# Process response and set stash values
-sub _process_matches {
+sub _process_query_response {
my ($self, $json) = @_;
# Process meta
@@ -96,6 +356,7 @@
# if ($benchmark && $benchmark =~ s/\s+(m)?s$//) {
# $benchmark = sprintf("%.2f", $benchmark) . ($1 ? $1 : '') . 's';
# };
+ #
# # Set benchmark
# $self->stash(benchmark => $benchmark);
@@ -139,420 +400,7 @@
};
-# Cleanup array of matches
-sub _map_matches {
- return c() unless $_[0];
- c(map { _map_match($_) } @{ shift() });
-};
-
-
-# Cleanup single match
-sub _map_match {
- my $match = shift or return;
-
- # Legacy match id
- if ($match->{matchID}) {
- $match->{matchID} =~ s/^match\-(?:[^!]+!|[^_]+_)[^\.]+?\.[^-]+?-// or
- $match->{matchID} =~ s!^match\-(?:[^\/]+\/){2}[^-]+?-!!;
- };
-
- return unless $match->{textSigle};
-
- # Set IDs based on the sigle
- (
- $match->{corpusID},
- $match->{docID},
- $match->{textID}
- ) = ($match->{textSigle} =~ /^([^_]+?)_+([^\.]+?)\.(.+?)$/);
-
- return $match;
-};
-
-
-# Query endpoint
-sub query {
- my $c = shift;
-
- # Validate user input
- my $v = $c->validation;
-
- # In case the user is not known, it is assumed, the user is not logged in
- my $user = $c->stash('user') // 'not_logged_in';
-
- $v->optional('q', 'trim');
- $v->optional('ql')->in(qw/poliqarp cosmas2 annis cql fcsql/);
- $v->optional('collection', 'trim'); # Legacy
- $v->optional('cq', 'trim');
- # $v->optional('action'); # action 'inspect' is no longer valid
- $v->optional('snippet');
- $v->optional('cutoff')->in(qw/true false/);
- $v->optional('count')->num(1, undef);
- $v->optional('p', 'trim')->num(1, undef); # Start page
- $v->optional('o', 'trim')->num(1, undef); # Offset
- $v->optional('context');
-
- # Get query
- my $query = $v->param('q');
-
-
- # TODO:
- # Check for validation errors!
-
- # No query
- unless ($query) {
- return $c->render($c->loc('Template_intro', 'intro'));
- };
-
- my %query = ();
- $query{q} = $v->param('q');
- $query{ql} = $v->param('ql') // 'poliqarp';
- $query{p} = $v->param('p') // 1; # Start page
- $query{count} = $v->param('count') // $c->items_per_page;
- $query{cq} = $v->param('cq') // $v->param('collection');
- $query{cutoff} = $v->param('cutoff');
- $query{context} = $v->param('context') // '40-t,40-t'; # 'base/s:p'/'paragraph'
-
- my $items_per_page = $c->items_per_page;
-
- # Set count
- if ($query{count} && $query{count} <= $c->items_per_page ) {
- $items_per_page = delete $query{count};
- };
-
- $c->stash(items_per_page => $items_per_page);
-
- # Set offset
- # From Mojolicious::Plugin::Search::Index
- $query{o} = $v->param('o') || ((($query{p} // 1) - 1) * ($items_per_page || 1));
-
-
- # already set by stash - or use plugin param
- # else {
- # $items_per_page = $c->stash('search.count') // $plugin->items_per_page
- # };
-
- # Set start page based on param
- #if ($query{p}) {
- # $index->start_page(delete $param{start_page});
- #}
- ## already set by stash
- #elsif ($c->stash('search.start_page')) {
- # $index->start_page($c->stash('search.start_page'));
- #};
-
-
- # Create remote request URL
- my $url = Mojo::URL->new($c->api);
- $url->path('search');
- $url->query(\%query);
-
- # Check if total results is cached
- my $total_results = -1;
- unless ($c->no_cache) {
-
- # Get total results value
- $total_results = $c->chi->get('total-' . $user . '-' . $url->to_string);
-
- # Set stash if cache exists
- $c->stash(total_results => $total_results) if $total_results;
- $c->app->log->debug('Get total result from cache');
-
- # Set cutoff unless already set
- $url->query({cutoff => 'true'});
- };
-
- # Establish 'search_results' taghelper
- # This is based on Mojolicious::Plugin::Search
- $c->app->helper(
- search_results => sub {
- my $c = shift;
-
- # This is a tag helper for templates
- my $cb = shift;
- if (!ref $cb || !(ref $cb eq 'CODE')) {
- $c->app->log->error('search_results expects a code block');
- return '';
- };
-
- my $coll = $c->stash('results');
-
- # Iterate over results
- my $string = $coll->map(
- sub {
- # Call hit callback
- # $c->stash('search.hit' => $_);
- local $_ = $_[0];
- return $cb->($_);
- })->join;
-
- # Remove hit from stash
- # delete $c->stash->{'search.hit'};
- return b($string);
- }
- );
-
- # Check if the request is cached
- my $url_string = $url->to_string;
-
- # Set api request for debugging
- $c->stash('api_request' => $url_string);
-
- # Debugging
- $c->app->log->debug('Search for ' . $url_string);
-
- # Check for cache
- my $json = $c->chi->get('matches-' . $user . '-' . $url_string);
-
- # Initialize promise object
- my $promise;
-
- # Result is cached
- if ($json) {
- $json->{cached} = 'true';
-
- # The promise is already satisfied by the cache
- $promise = Mojo::Promise->new->resolve($json);
- }
-
- # Retrieve from URL
- else {
-
- # Wrap a user agent method with a promise
- $promise = $c->user->auth_request_p(get => $url)
-
- # TODO: Better use a single then
- ->then(\&_catch_http_errors)
- ->then(
- sub {
- my $json = shift->json;
-
- unless ($json) {
- return Mojo::Promise->new->reject([
- [undef, 'JSON response is invalid']
- ]);
- };
-
- $c->stash('api_response' => $json);
- return $json;
- })
- ->then(\&_catch_koral_errors)
- ;
- };
-
- # Wait for rendering
- $c->render_later;
-
- # Choose the snippet based on the parameter
- my $template = scalar $v->param('snippet') ? 'snippet' : 'search2';
- $c->stash(template => $template);
-
- # Process response
- $promise->then(
- sub {
- my $json = shift;
-
- # Prepare warnings
- $c->_notify_on_warnings($json->{warnings}) if $json->{warnings};
-
- # Cache total results
- # The stash is set in case the total results value is from the cache,
- # so in that case, it does not need to be cached again
- my $total_results = $c->stash('total_results');
- if (!$total_results) {
-
- # There are results to remember
- if ($json->{meta}->{totalResults} >= 0) {
-
- # Remove cutoff requirement again
- $url->query([cutoff => 'true']);
-
- $total_results = $json->{meta}->{totalResults};
- $c->stash(total_results => $total_results);
-
- # Set cache
- $c->chi->set(
- 'total-' . $user . '-' . $url->to_string => $total_results
- );
- };
- }
- else {
- $c->stash(total_results => -1);
- }
-
- $c->stash(total_pages => 0);
-
- # Set total pages
- # From Mojolicious::Plugin::Search::Index
- if ($total_results > 0) {
- $c->stash(total_pages => ceil($total_results / ($c->stash('items_per_page') || 1)));
- };
-
- # Cache result
- $c->chi->set('matches-' . $user . '-' . $url_string => $json);
-
- # Process match results
- return $c->_process_matches($json);
- }
-
- # Render template
- )->then(
- sub {
- return $c->render(
- q => $query,
- ql => $query{ql},
- start_page => $query{p},
- );
- }
-
- # Deal with errors
- )->catch(
- sub {
- $c->_notify_on_errors(shift);
- return $c->render(
- results => c(),
- q => $query,
- ql => $query{ql},
- start_page => 1
- );
- }
- )
-
- # Start IOLoop
- ->wait;
-
-
- return 1;
-};
-
-
-sub match_info {
- my $c = shift;
-
- # Validate user input
- my $v = $c->validation;
- $v->optional('foundry');
- $v->optional('layer');
- $v->optional('spans')->in(qw/true false/);
-
- # Old API foundry/layer usage
- my $foundry = '*';
- my %query = (foundry => '*');
- if ($v->param('foundry')) {
- $query{foundry} = $v->param('foundry');
- $query{layer} = $v->param('layer') if $v->param('layer');
- $query{spans} = $v->param('spans') if $v->param('spans');
- };
-
- # Create new request API
- my $url = Mojo::URL->new($c->api);
-
- # Use stash information to create url path
- $url->path(
- join('/', (
- 'corpus',
- $c->stash('corpus_id'),
- $c->stash('doc_id'),
- $c->stash('text_id'),
- $c->stash('match_id'),
- 'matchInfo'
- ))
- );
-
- # Set query parameters
- $url->query(\%query);
-
- $c->app->log->debug('Text info: ' . $url);
-
- $c->render_later;
-
- # TODO: Add caching!
- $c->user->auth_request_p(
- get => $url
- )
- ->then(\&_catch_http_errors)
- ->then(
- sub {
- my $json = shift->json;
- unless ($json) {
- return Mojo::Promise->new->reject([
- [undef, 'JSON response is invalid']
- ]);
- };
-
- $c->stash(results => _map_match($json));
-# $c->stash(results => $json);
- return $json;
- }
- )
- ->then(
- \&_catch_koral_errors
- )
- # Deal with errors
- ->catch(
- sub {
- $c->_notify_on_errors(shift);
- }
- )
-
- ->finally(
- sub {
- # Add notifications to the matching json
- # TODO: There should be a special notification engine doing that!
-
- my $json = $c->stash('results');
- my $notes = $c->notifications(json => $json);
- return $c->render(
- json => $notes,
- status => 200
- );
- }
- )
-
- # Start IOLoop
- ->wait;
-
- return 1;
-};
-
1;
__END__
-
- # Async
- $c->render_later;
-
- # Use the API for fetching matching information non-blocking
- $c->search->match(
- corpus_id => $c->stash('corpus_id'),
- doc_id => $c->stash('doc_id'),
- text_id => $c->stash('text_id'),
- match_id => $c->stash('match_id'),
- %query,
-
- # Callback for async search
- sub {
- my $index = shift;
- return $c->respond_to(
-
- # Render json if requested
- json => sub {
- # Add notifications to the matching json
- # TODO: There should be a special notification engine doing that!
- my $notes = $c->notifications(json => $index->results->[0]);
- $c->render(
- json => $notes,
- status => $index->status
- );
- },
-
- # Render html if requested
- html => sub {
- return $c->render(
- layout => 'default',
- template => 'match_info'
- )
- }
- );
- }
- );
diff --git a/lib/Kalamar/Plugin/KalamarErrors.pm b/lib/Kalamar/Plugin/KalamarErrors.pm
new file mode 100644
index 0000000..fd4a174
--- /dev/null
+++ b/lib/Kalamar/Plugin/KalamarErrors.pm
@@ -0,0 +1,117 @@
+package Kalamar::Plugin::KalamarErrors;
+use Mojo::Base 'Mojolicious::Plugin';
+
+
+# Register error plugin
+sub register {
+ my ($plugin, $mojo) = @_;
+
+
+ # Notify on warnings
+ $mojo->helper(
+ notify_on_warnings => sub {
+ my ($c, $json) = @_;
+
+ my $warnings = $json->{warnings};
+
+ return unless $warnings;
+
+ # TODO: Check for ref!
+ foreach my $w (@$warnings) {
+ $c->notify(
+ warn =>
+ ($w->[0] ? $w->[0] . ': ' : '') .
+ $w->[1]
+ );
+ };
+
+ return 1;
+ }
+ );
+
+ # Notify on errors
+ $mojo->helper(
+ notify_on_errors => sub {
+ my ($c, $json) = @_;
+
+ my $errors = $json->{errors};
+
+ return unless $errors;
+
+ foreach my $e (@$errors) {
+ $c->notify(
+ error =>
+ ($e->[0] ? $e->[0] . ': ' : '') .
+ ($e->[1] || 'Unknown')
+ );
+ };
+
+ return 1;
+ }
+ );
+
+ # Catch connection errors
+ $mojo->helper(
+ catch_errors_and_warnings => sub {
+ my ($c, $tx) = @_;
+
+ my $err = $tx->error;
+
+ if ($err && $err->{code} != 500) {
+ $c->stash(status => $err->{code});
+ };
+
+ # Check the response
+ my $res = $tx->res;
+ my $json;
+ $json = $res->json if $res->body;
+
+ # There is no json and no error
+ if (!$json && !$err) {
+
+ $c->notify(error => 'JSON response is invalid');
+ return Mojo::Promise->new->reject;
+ };
+
+ # There is json
+ if ($json) {
+ $c->stash(api_response => $json);
+
+ # TODO:
+ # Check for references of errors and warnings!
+
+ # There are errors
+ if ($c->notify_on_errors($json)) {
+
+ # Return on errors - ignore warnings
+ return Mojo::Promise->new->reject;
+ };
+
+ # Notify on warnings
+ $c->notify_on_warnings($json);
+
+ # What does status mean?
+ if ($json->{status}) {
+
+ $c->notify(error => 'Middleware error ' . $json->{'status'});
+ return Mojo::Promise->new->reject;
+ };
+ }
+
+ # There is an error but no json
+ else {
+
+ # Send rejection promise
+ $c->notify(error => $err->{code} . ': ' . $err->{message});
+ return Mojo::Promise->new->reject;
+ };
+
+ return $json;
+ }
+ );
+};
+
+
+
+
+1;
diff --git a/lib/Kalamar/Plugin/KalamarHelpers.pm b/lib/Kalamar/Plugin/KalamarHelpers.pm
index 0fee4c8..b6a67c9 100644
--- a/lib/Kalamar/Plugin/KalamarHelpers.pm
+++ b/lib/Kalamar/Plugin/KalamarHelpers.pm
@@ -240,6 +240,42 @@
$c->stash('kalamar.test_port' => 0);
return 0;
});
+
+ # Establish 'search_results' taghelper
+ # This is based on Mojolicious::Plugin::Search
+ $mojo->helper(
+ search_results2 => sub {
+ my $c = shift;
+
+ # This is a tag helper for templates
+ my $cb = shift;
+ if (!ref $cb || !(ref $cb eq 'CODE')) {
+ $c->app->log->error('search_results expects a code block');
+ return '';
+ };
+
+ my $coll = $c->stash('results');
+
+ # Iterate over results
+ my $string = $coll->map(
+ sub {
+ # Call hit callback
+ # $c->stash('search.hit' => $_);
+ local $_ = $_[0];
+ return $cb->($_);
+ })->join;
+
+ # Remove hit from stash
+ # delete $c->stash->{'search.hit'};
+ return b($string);
+ }
+ );
+
+ $mojo->helper(
+ 'korap.api' => sub {
+ return shift->config('Search')->{api};
+ }
+ );
};
diff --git a/t/match_info.t b/t/match_info.t
index 3e4b72c..1b6f8ef 100644
--- a/t/match_info.t
+++ b/t/match_info.t
@@ -50,7 +50,7 @@
$t->get_ok('/corpus2/fail/x/x/p0-0')
->status_is(200)
->json_is('/notifications/0/0', 'error')
- ->json_is('/notifications/0/1', '500: Internal Server Error')
+ ->json_is('/notifications/0/1', 'Unable to load query response from /home/ndiewald/Repositories/korap-git/Kalamar/t/fixtures/response_matchinfo_fail_x_x_p0-0.json')
;
# TODO:
@@ -64,10 +64,11 @@
# TODO:
# It's surprising, that it doesn't return a 404!
$t->get_ok('/corpus2/notfound2/X/X/p0-1')
- ->status_is(200)
+ ->status_is(404)
->json_is('/notifications/0/0', 'error')
->json_is('/notifications/0/1', '404: Not Found')
;
done_testing;
+__END__
diff --git a/t/query.t b/t/query.t
index bf82ba2..4bc97dc 100644
--- a/t/query.t
+++ b/t/query.t
@@ -67,4 +67,11 @@
->text_is('li:nth-of-type(1) p.ref span.sigle', '[GOE/AGI/00000]')
;
+
+$t->get_ok('/q2?q=[orth=das')
+ ->status_is(400)
+ ->text_is('div.notify-error:nth-of-type(1)', '302: Parantheses/brackets unbalanced.')
+ ->text_like('div.notify-error:nth-of-type(2)', qr!302: Could not parse query .+? \[orth=das.+?!)
+ ;
+
done_testing;
diff --git a/templates/search2.html.ep b/templates/search2.html.ep
index 200d693..5a72ca5 100644
--- a/templates/search2.html.ep
+++ b/templates/search2.html.ep
@@ -1,8 +1,7 @@
% layout 'main', title => loc('searchtitle', q => stash('q'), ql => stash('ql')), schematype => 'SearchResultsPage';
-
<div id="resultinfo" <% if (stash('results')->size) { %> class="found"<%} %>>
<div id="pagination"><%= pagination(stash('start_page'), stash('total_pages'), url_with->query(['p' => '{page}'])) =%></div>
-% my $found = stash('total_results');
+% my $found = stash('total_results') // 0;
<p class="found">\
% if ($found != -1) {
% my $found_text = loc('numf', number => $found);
@@ -20,9 +19,9 @@
%= include 'query2'
<div id="search">
-% if (stash('total_results') != 0 && stash('results')->size) {
+% if (stash('results')->size && stash('total_results') != 0) {
<ol class="align-left">
-%= search_results begin
+%= search_results2 begin
%= include 'match', match => $_
% end
</ol>