View | Details | Raw Unified | Return to bug 18206
Collapse All | Expand All

(-)a/Koha/Exceptions.pm (+74 lines)
Lines 69-72 use Exception::Class ( Link Here
69
    }
69
    }
70
);
70
);
71
71
72
use Mojo::JSON;
73
use Scalar::Util qw( blessed );
74
75
=head1 NAME
76
77
Koha::Exceptions
78
79
=head1 API
80
81
=head2 Class Methods
82
83
=head3 rethrow_exception
84
85
    try {
86
        # ..
87
    } catch {
88
        # ..
89
        Koha::Exceptions::rethrow_exception($e);
90
    }
91
92
A function for re-throwing any given exception C<$e>. This also includes other
93
exceptions than Koha::Exceptions.
94
95
=cut
96
97
sub rethrow_exception {
98
    my ($e) = @_;
99
100
    die $e unless blessed($e);
101
    die $e if ref($e) eq 'Mojo::Exception'; # Mojo::Exception is rethrown by die
102
    die $e unless $e->can('rethrow');
103
    $e->rethrow;
104
}
105
106
=head3 to_str
107
108
A function for representing any given exception C<$e> as string.
109
110
C<to_str> is aware of some of the most common exceptions and how to stringify
111
112
them, however, also stringifies unknown exceptions by encoding them into JSON.
113
114
=cut
115
116
sub to_str {
117
    my ($e) = @_;
118
119
    return (ref($e) ? ref($e) ." => " : '') . _stringify_exception($e);
120
}
121
122
123
124
sub _stringify_exception {
125
    my ($e) = @_;
126
127
    return $e unless blessed($e);
128
129
    # Stringify a known exception
130
    return $e->to_string      if ref($e) eq 'Mojo::Exception';
131
    return $e->{'msg'}        if ref($e) eq 'DBIx::Class::Exception';
132
    return $e->error          if $e->isa('Koha::Exception');
133
134
    # Stringify an unknown exception by attempting to use some methods
135
    return $e->to_str         if $e->can('to_str');
136
    return $e->to_string      if $e->can('to_string');
137
    return $e->error          if $e->can('error');
138
    return $e->message        if $e->can('message');
139
    return $e->string         if $e->can('string');
140
    return $e->str            if $e->can('str');
141
142
    # Finally, handle unknown exception by encoding it into JSON text
143
    return Mojo::JSON::encode_json({%$e});
144
}
145
72
1;
146
1;
(-)a/Koha/REST/V1.pm (+28 lines)
Lines 47-52 sub startup { Link Here
47
        shift->req->url->base->path('/');
47
        shift->req->url->base->path('/');
48
    });
48
    });
49
49
50
    $self->app->hook(before_render => \&default_exception_handling);
51
50
    # Force charset=utf8 in Content-Type header for JSON responses
52
    # Force charset=utf8 in Content-Type header for JSON responses
51
    $self->types->type( json    => 'application/json; charset=utf8' );
53
    $self->types->type( json    => 'application/json; charset=utf8' );
52
    # MARC-related types
54
    # MARC-related types
Lines 94-97 sub startup { Link Here
94
    $self->plugin( 'Koha::REST::Plugin::Objects' );
96
    $self->plugin( 'Koha::REST::Plugin::Objects' );
95
}
97
}
96
98
99
=head3 default_exception_handling
100
101
A before_render hook for handling default exceptions.
102
103
=cut
104
105
sub default_exception_handling {
106
    my ($c, $args) = @_;
107
108
    if ($args->{exception} && $args->{exception}->{message}) {
109
        my $e = $args->{exception}->{message};
110
        $c->app->log->error(Koha::Exceptions::to_str($e));
111
        %$args = (
112
            status => 500,
113
            # TODO: Do we want a configuration for displaying either
114
            # a detailed description of the error or simply a "Something
115
            # went wrong, check the logs."? Now that we can stringify all
116
            # exceptions with Koha::Exceptions::to_str($e), we could also
117
            # display the detailed error if some DEBUG variable is enabled.
118
            # Of course the error is still logged if log4perl is configured
119
            # appropriately...
120
            json => { error => 'Something went wrong, check the logs.' }
121
        );
122
    }
123
}
124
97
1;
125
1;
(-)a/t/Koha/Exceptions.t (-1 / +55 lines)
Lines 17-25 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 4;
20
use Test::More tests => 6;
21
use Test::MockObject;
21
use Test::MockObject;
22
use Test::Exception;
22
use Test::Exception;
23
use Koha::Exceptions;
24
use Mojo::Exception;
25
use DBIx::Class::Exception;
23
26
24
subtest 'Koha::Exceptions::Hold tests' => sub {
27
subtest 'Koha::Exceptions::Hold tests' => sub {
25
28
Lines 128-130 subtest 'Koha::Exceptions::Metadata tests' => sub { Link Here
128
        'Exception is thrown :-D';
131
        'Exception is thrown :-D';
129
    is( "$@", 'Manual message exception', 'Exception not stringified if manually passed' );
132
    is( "$@", 'Manual message exception', 'Exception not stringified if manually passed' );
130
};
133
};
134
135
subtest 'rethrow_exception() tests' => sub {
136
    plan tests => 4;
137
138
    my $e = Koha::Exceptions::Exception->new(
139
        error => 'houston, we have a problem'
140
    );
141
    eval { Koha::Exceptions::rethrow_exception($e) };
142
    is(ref($@), 'Koha::Exceptions::Exception', ref($@));
143
144
    eval { DBIx::Class::Exception->throw('dang') };
145
    $e = $@;
146
    eval { Koha::Exceptions::rethrow_exception($e) };
147
    is(ref($@), 'DBIx::Class::Exception', ref($@));
148
149
    eval { Mojo::Exception->throw('dang') };
150
    $e = $@;
151
    eval { Koha::Exceptions::rethrow_exception($e) };
152
    is(ref($@), 'Mojo::Exception', ref($@));
153
154
    eval { die "wow" };
155
    $e = $@;
156
    eval { Koha::Exceptions::rethrow_exception($e) };
157
    like($@, qr/^wow at .*Exceptions.t line \d+\.$/, $@);
158
};
159
160
subtest 'to_str() tests' => sub {
161
    plan tests => 4;
162
163
    my $text;
164
    eval { Koha::Exceptions::Exception->throw(error => 'dang') };
165
    is($text = Koha::Exceptions::to_str($@),
166
       'Koha::Exceptions::Exception => dang', $text);
167
    eval { DBIx::Class::Exception->throw('dang') };
168
    like($text = Koha::Exceptions::to_str($@),
169
       qr/DBIx::Class::Exception => .*dang/, $text);
170
    eval { Mojo::Exception->throw('dang') };
171
    is($text = Koha::Exceptions::to_str($@),
172
       'Mojo::Exception => dang', $text);
173
    eval {
174
        my $exception = {
175
            what => 'test unknown exception',
176
            otherstuffs => 'whatever'
177
        };
178
        bless $exception, 'Unknown::Exception';
179
        die $exception;
180
    };
181
    is($text = Koha::Exceptions::to_str($@), 'Unknown::Exception => '
182
       .'{"otherstuffs":"whatever","what":"test unknown exception"}', $text);
183
};
184
(-)a/t/db_dependent/api/v1.t (-1 / +153 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Test::More tests => 1;
21
use Test::Mojo;
22
use Test::Warn;
23
24
use t::lib::Mocks;
25
use Koha::Exceptions;
26
27
use Log::Log4perl;
28
use Mojolicious::Lite;
29
use Try::Tiny;
30
31
my $config = {
32
    'log4perl.logger.rest.Koha.REST.V1' => 'ERROR, TEST',
33
    'log4perl.appender.TEST' => 'Log::Log4perl::Appender::TestBuffer',
34
    'log4perl.appender.TEST.layout' => 'SimpleLayout',
35
};
36
t::lib::Mocks::mock_config('log4perl_conf', $config);
37
38
my $remote_address = '127.0.0.1';
39
my $t              = Test::Mojo->new('Koha::REST::V1');
40
41
subtest 'default_exception_handling() tests' => sub {
42
    plan tests => 5;
43
44
    add_default_exception_routes($t);
45
46
    my $appender = Log::Log4perl->appenders->{TEST};
47
48
    subtest 'Mojo::Exception' => sub {
49
        plan tests => 4;
50
51
        $t->get_ok('/default_exception_handling/mojo')
52
          ->status_is(500)
53
          ->json_is('/error' => 'Something went wrong, check the logs.');
54
55
        like($appender->buffer, qr/ERROR - test mojo exception/,
56
             'Found test mojo exception in log');
57
        $appender->{appender}->{buffer} = undef;
58
    };
59
60
    subtest 'die() outside try { } catch { };' => sub {
61
        plan tests => 4;
62
63
        $t->get_ok('/default_exception_handling/dieoutsidetrycatch')
64
          ->status_is(500)
65
          ->json_is('/error' => 'Something went wrong, check the logs.');
66
        like($appender->buffer, qr/ERROR - die outside try-catch/,
67
             'Found die outside try-catch in log');
68
        $appender->{appender}->{buffer} = undef;
69
    };
70
71
    subtest 'DBIx::Class::Exception' => sub {
72
        plan tests => 4;
73
74
        $t->get_ok('/default_exception_handling/dbix')
75
          ->status_is(500)
76
          ->json_is('/error' => 'Something went wrong, check the logs.');
77
        like($appender->buffer, qr/ERROR - DBIx::Class::Exception => .* test dbix exception/,
78
             'Found test dbix exception in log');
79
        $appender->{appender}->{buffer} = undef;
80
    };
81
82
    subtest 'Koha::Exceptions::Exception' => sub {
83
        plan tests => 4;
84
85
        $t->get_ok('/default_exception_handling/koha')
86
          ->status_is(500)
87
          ->json_is('/error' => 'Something went wrong, check the logs.');
88
        like($appender->buffer, qr/ERROR - Koha::Exceptions::Exception => test koha exception/,
89
             'Found test koha exception in log');
90
        $appender->{appender}->{buffer} = undef;
91
    };
92
93
    subtest 'Unknown exception' => sub {
94
        plan tests => 4;
95
96
        $t->get_ok('/default_exception_handling/unknown')
97
          ->status_is(500)
98
          ->json_is('/error' => 'Something went wrong, check the logs.');
99
        like($appender->buffer, qr/ERROR - Unknown::Exception::OhNo => \{"what":"test unknown exception"\}/,
100
             'Found test unknown exception in log');
101
        $appender->{appender}->{buffer} = undef;
102
    };
103
};
104
105
sub add_default_exception_routes {
106
    my ($t) = @_;
107
108
    # Mojo::Exception
109
    $t->app->routes->get('/default_exception_handling/mojo' => sub {
110
        try {
111
            die "test mojo exception";
112
        } catch {
113
            Koha::Exceptions::rethrow_exception($_);
114
        };
115
    });
116
117
    # die outside try-catch
118
    $t->app->routes->get('/default_exception_handling/dieoutsidetrycatch' => sub {
119
        die "die outside try-catch";
120
    });
121
122
    # DBIx::Class::Exception
123
    $t->app->routes->get('/default_exception_handling/dbix' => sub {
124
        package Koha::REST::V1::Test;
125
        try {
126
            DBIx::Class::Exception->throw('test dbix exception');
127
        } catch {
128
            Koha::Exceptions::rethrow_exception($_);
129
        };
130
    });
131
132
    # Koha::Exceptions::Exception
133
    $t->app->routes->get('/default_exception_handling/koha' => sub {
134
        try {
135
            Koha::Exceptions::Exception->throw('test koha exception');
136
        } catch {
137
            Koha::Exceptions::rethrow_exception($_);
138
        };
139
    });
140
141
    # Unknown exception
142
    $t->app->routes->get('/default_exception_handling/unknown' => sub {
143
        try {
144
            my $exception = { what => 'test unknown exception'};
145
            bless $exception, 'Unknown::Exception::OhNo';
146
            die $exception;
147
        } catch {
148
            Koha::Exceptions::rethrow_exception($_);
149
        };
150
    });
151
}
152
153
1;

Return to bug 18206