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

(-)a/Koha/Exceptions.pm (+71 lines)
Lines 53-56 use Exception::Class ( Link Here
53
    }
53
    }
54
);
54
);
55
55
56
use Mojo::JSON;
57
use Scalar::Util qw( blessed );
58
59
=head1 NAME
60
61
Koha::Exceptions
62
63
=head1 API
64
65
=head2 Class Methods
66
67
=head3 rethrow_exception
68
69
    try {
70
        # ..
71
    } catch {
72
        # ..
73
        Koha::Exceptions::rethrow_exception($e);
74
    }
75
76
A function for re-throwing any given exception C<$e>. This also includes other
77
exceptions than Koha::Exceptions.
78
79
=cut
80
81
sub rethrow_exception {
82
    my ($e) = @_;
83
84
    die $e unless blessed($e);
85
    die $e if ref($e) eq 'Mojo::Exception'; # Mojo::Exception is rethrown by die
86
    die $e unless $e->can('rethrow');
87
    $e->rethrow;
88
}
89
90
=head3 to_str
91
92
A function for representing any given exception C<$e> as string.
93
94
C<to_str> is aware of some of the most common exceptions and how to stringify
95
them, however, also stringifies unknown exceptions by encoding them into JSON.
96
97
=cut
98
99
sub to_str {
100
    my ($e) = @_;
101
102
    return (ref($e) ? ref($e) ." => " : '') . _stringify_exception($e);
103
}
104
105
sub _stringify_exception {
106
    my ($e) = @_;
107
108
    return $e unless blessed($e);
109
110
    # Stringify a known exception
111
    return $e->to_string      if ref($e) eq 'Mojo::Exception';
112
    return $e->{'msg'}        if ref($e) eq 'DBIx::Class::Exception';
113
    return $e->error          if $e->isa('Koha::Exception');
114
115
    # Stringify an unknown exception by attempting to use some methods
116
    return $e->to_str         if $e->can('to_str');
117
    return $e->to_string      if $e->can('to_string');
118
    return $e->error          if $e->can('error');
119
    return $e->message        if $e->can('message');
120
    return $e->string         if $e->can('string');
121
    return $e->str            if $e->can('str');
122
123
    # Finally, handle unknown exception by encoding it into JSON text
124
    return Mojo::JSON::encode_json({%$e});
125
}
126
56
1;
127
1;
(-)a/Koha/REST/V1.pm (+28 lines)
Lines 56-61 sub startup { Link Here
56
        $self->secrets([$secret_passphrase]);
56
        $self->secrets([$secret_passphrase]);
57
    }
57
    }
58
58
59
    $self->app->hook(before_render => \&default_exception_handling);
60
59
    $self->plugin(OpenAPI => {
61
    $self->plugin(OpenAPI => {
60
        url => $self->home->rel_file("api/v1/swagger/swagger.json"),
62
        url => $self->home->rel_file("api/v1/swagger/swagger.json"),
61
        route => $self->routes->under('/api/v1')->to('Auth#under'),
63
        route => $self->routes->under('/api/v1')->to('Auth#under'),
Lines 65-68 sub startup { Link Here
65
    });
67
    });
66
}
68
}
67
69
70
=head3 default_exception_handling
71
72
A before_render hook for handling default exceptions.
73
74
=cut
75
76
sub default_exception_handling {
77
    my ($c, $args) = @_;
78
79
    if ($args->{exception} && $args->{exception}->{message}) {
80
        my $e = $args->{exception}->{message};
81
        $c->app->log->error(Koha::Exceptions::to_str($e));
82
        %$args = (
83
            status => 500,
84
            # TODO: Do we want a configuration for displaying either
85
            # a detailed description of the error or simply a "Something
86
            # went wrong, check the logs."? Now that we can stringify all
87
            # exceptions with Koha::Exceptions::to_str($e), we could also
88
            # display the detailed error if some DEBUG variable is enabled.
89
            # Of course the error is still logged if log4perl is configured
90
            # appropriately...
91
            json => { error => 'Something went wrong, check the logs.' }
92
        );
93
    }
94
}
95
68
1;
96
1;
(-)a/t/Koha/Exceptions.t (+74 lines)
Line 0 Link Here
1
#!/usr/bin/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 => 2;
21
22
use Koha::Exceptions;
23
use Mojo::Exception;
24
use DBIx::Class::Exception;
25
26
subtest 'rethrow_exception() tests' => sub {
27
    plan tests => 4;
28
29
    my $e = Koha::Exceptions::Exception->new(
30
        error => 'houston, we have a problem'
31
    );
32
    eval { Koha::Exceptions::rethrow_exception($e) };
33
    is(ref($@), 'Koha::Exceptions::Exception', ref($@));
34
35
    eval { DBIx::Class::Exception->throw('dang') };
36
    $e = $@;
37
    eval { Koha::Exceptions::rethrow_exception($e) };
38
    is(ref($@), 'DBIx::Class::Exception', ref($@));
39
40
    eval { Mojo::Exception->throw('dang') };
41
    $e = $@;
42
    eval { Koha::Exceptions::rethrow_exception($e) };
43
    is(ref($@), 'Mojo::Exception', ref($@));
44
45
    eval { die "wow" };
46
    $e = $@;
47
    eval { Koha::Exceptions::rethrow_exception($e) };
48
    like($@, qr/^wow at .*Exceptions.t line \d+\.$/, $@);
49
};
50
51
subtest 'to_str() tests' => sub {
52
    plan tests => 4;
53
54
    my $text;
55
    eval { Koha::Exceptions::Exception->throw(error => 'dang') };
56
    is($text = Koha::Exceptions::to_str($@),
57
       'Koha::Exceptions::Exception => dang', $text);
58
    eval { DBIx::Class::Exception->throw('dang') };
59
    like($text = Koha::Exceptions::to_str($@),
60
       qr/DBIx::Class::Exception => .*dang/, $text);
61
    eval { Mojo::Exception->throw('dang') };
62
    is($text = Koha::Exceptions::to_str($@),
63
       'Mojo::Exception => dang', $text);
64
    eval {
65
        my $exception = {
66
            what => 'test unknown exception',
67
            otherstuffs => 'whatever'
68
        };
69
        bless $exception, 'Unknown::Exception';
70
        die $exception;
71
    };
72
    is($text = Koha::Exceptions::to_str($@), 'Unknown::Exception => '
73
       .'{"otherstuffs":"whatever","what":"test unknown exception"}', $text);
74
};
(-)a/t/db_dependent/api/v1.t (-1 / +154 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
my $tx;
41
42
subtest 'default_exception_handling() tests' => sub {
43
    plan tests => 5;
44
45
    add_default_exception_routes($t);
46
47
    my $appender = Log::Log4perl->appenders->{TEST};
48
49
    subtest 'Mojo::Exception' => sub {
50
        plan tests => 4;
51
52
        $t->get_ok('/default_exception_handling/mojo')
53
          ->status_is(500)
54
          ->json_is('/error' => 'Something went wrong, check the logs.');
55
56
        like($appender->buffer, qr/ERROR - test mojo exception/,
57
             'Found test mojo exception in log');
58
        $appender->{appender}->{buffer} = undef;
59
    };
60
61
    subtest 'die() outside try { } catch { };' => sub {
62
        plan tests => 4;
63
64
        $t->get_ok('/default_exception_handling/dieoutsidetrycatch')
65
          ->status_is(500)
66
          ->json_is('/error' => 'Something went wrong, check the logs.');
67
        like($appender->buffer, qr/ERROR - die outside try-catch/,
68
             'Found die outside try-catch in log');
69
        $appender->{appender}->{buffer} = undef;
70
    };
71
72
    subtest 'DBIx::Class::Exception' => sub {
73
        plan tests => 4;
74
75
        $t->get_ok('/default_exception_handling/dbix')
76
          ->status_is(500)
77
          ->json_is('/error' => 'Something went wrong, check the logs.');
78
        like($appender->buffer, qr/ERROR - DBIx::Class::Exception => .* test dbix exception/,
79
             'Found test dbix exception in log');
80
        $appender->{appender}->{buffer} = undef;
81
    };
82
83
    subtest 'Koha::Exceptions::Exception' => sub {
84
        plan tests => 4;
85
86
        $t->get_ok('/default_exception_handling/koha')
87
          ->status_is(500)
88
          ->json_is('/error' => 'Something went wrong, check the logs.');
89
        like($appender->buffer, qr/ERROR - Koha::Exceptions::Exception => test koha exception/,
90
             'Found test koha exception in log');
91
        $appender->{appender}->{buffer} = undef;
92
    };
93
94
    subtest 'Unknown exception' => sub {
95
        plan tests => 4;
96
97
        $t->get_ok('/default_exception_handling/unknown')
98
          ->status_is(500)
99
          ->json_is('/error' => 'Something went wrong, check the logs.');
100
        like($appender->buffer, qr/ERROR - Unknown::Exception::OhNo => {"what":"test unknown exception"}/,
101
             'Found test unknown exception in log');
102
        $appender->{appender}->{buffer} = undef;
103
    };
104
};
105
106
sub add_default_exception_routes {
107
    my ($t) = @_;
108
109
    # Mojo::Exception
110
    $t->app->routes->get('/default_exception_handling/mojo' => sub {
111
        try {
112
            die "test mojo exception";
113
        } catch {
114
            Koha::Exceptions::rethrow_exception($_);
115
        };
116
    });
117
118
    # die outside try-catch
119
    $t->app->routes->get('/default_exception_handling/dieoutsidetrycatch' => sub {
120
        die "die outside try-catch";
121
    });
122
123
    # DBIx::Class::Exception
124
    $t->app->routes->get('/default_exception_handling/dbix' => sub {
125
        package Koha::REST::V1::Test;
126
        try {
127
            DBIx::Class::Exception->throw('test dbix exception');
128
        } catch {
129
            Koha::Exceptions::rethrow_exception($_);
130
        };
131
    });
132
133
    # Koha::Exceptions::Exception
134
    $t->app->routes->get('/default_exception_handling/koha' => sub {
135
        try {
136
            Koha::Exceptions::Exception->throw('test koha exception');
137
        } catch {
138
            Koha::Exceptions::rethrow_exception($_);
139
        };
140
    });
141
142
    # Unknown exception
143
    $t->app->routes->get('/default_exception_handling/unknown' => sub {
144
        try {
145
            my $exception = { what => 'test unknown exception'};
146
            bless $exception, 'Unknown::Exception::OhNo';
147
            die $exception;
148
        } catch {
149
            Koha::Exceptions::rethrow_exception($_);
150
        };
151
    });
152
}
153
154
1;

Return to bug 18206