File Coverage

blib/lib/WebService/Hydra/Exception.pm
Criterion Covered Total %
statement 38 38 100.0
branch 6 8 75.0
condition 2 3 66.6
subroutine 12 12 100.0
pod 1 4 25.0
total 59 65 90.7


line stmt bran cond sub pod time code
1             package WebService::Hydra::Exception;
2              
3 18     18   439350 use strict;
  18         41  
  18         808  
4 18     18   106 use warnings;
  18         35  
  18         990  
5              
6 18     18   898 use Object::Pad;
  18         13498  
  18         121  
7              
8             class WebService::Hydra::Exception;
9              
10 18     18   17280 use Module::Load;
  18         41186  
  18         146  
11 18     18   9027 use JSON::MaybeUTF8 qw(encode_json_text);
  18         187424  
  18         1638  
12 18     18   20720 use Log::Any qw($log);
  18         194299  
  18         145  
13              
14             our $VERSION = '0.005'; ## VERSION
15              
16             =head1 NAME
17              
18             WebService::Hydra::Exception - Base class for all Hydra Exceptions, loading all possible exception types.
19              
20             =head1 DESCRIPTION
21              
22             The base class cannot be instantiated directly, and it dynamically loads all exception types within WebService::Hydra::Exception::* namespace.
23              
24             =cut
25              
26 18     18   44515 use Scalar::Util qw(blessed);
  18         43  
  18         33320  
27              
28             # Field definitions commonly inherited by all subclasses
29 13     13 0 6584 field $message :param :reader = '';
30 13     13 0 71 field $category :param :reader = '';
  13         55  
31 13     2 0 76 field $details :param :reader = [];
  2         2  
32              
33 2         10 BUILD {
34             die ref($self) . " is a base class and cannot be instantiated directly." if ref($self) eq __PACKAGE__;
35             }
36              
37             =head1 Methods
38              
39             =head2 throw
40              
41             Instantiates a new exception and throws it (using L).
42              
43             =cut
44              
45             sub throw {
46 13     13 1 993 my ($class, @args) = @_;
47 13 100 66     67 die "$class is a base class and cannot be thrown directly." if (blessed($class) || $class) eq __PACKAGE__;
48 12 100       45 my $self = blessed($class) ? $class : $class->new(@args);
49 12         137 die $self;
50             }
51              
52             =head2 as_string
53              
54             Returns a string representation of the exception.
55              
56             =cut
57              
58             method as_string {
59             my $string = blessed($self);
60             my @substrings = ();
61             push @substrings, "Category=$category" if $category;
62             push @substrings, "Message=$message" if $message;
63             push @substrings, "Details=" . encode_json_text($details) if @$details;
64             $string .= "(" . join(", ", @substrings) . ")" if @substrings;
65             return $string;
66             }
67              
68             =head2 as_json
69              
70             Returns a JSON string representation of the exception.
71              
72             =cut
73              
74             method as_json {
75             my $data = {
76             Exception => blessed($self),
77             Category => $self->category,
78             Message => $self->message,
79             Details => $self->details,
80             };
81             return encode_json_text($data);
82             }
83              
84             =head2 log
85              
86             Logs the exception using Log::Any and increments a stats counter.
87              
88             =cut
89              
90             method log {
91             $log->errorf("Exception: %s", $self->as_string);
92             my $stats_name = blessed($self);
93             $stats_name =~ s/::/./g;
94             }
95              
96             # Exception class names explicitly listed
97             my @all_exceptions = qw(
98             HydraServiceUnreachable
99             FeatureUnavailable
100             HydraRequestError
101             InvalidLoginChallenge
102             InvalidLogoutChallenge
103             InvalidLoginRequest
104             TokenExchangeFailed
105             InvalidIdToken
106             InvalidConsentChallenge
107             InternalServerError
108             RevokeLoginSessionsFailed
109             InvalidToken
110             InvalidClaims
111             );
112              
113             =head2 import
114              
115             The import method dynamically loads specific exceptions, or all by default.
116              
117             =cut
118              
119             sub import {
120 14     14   7089 my ($class, @exceptions) = @_;
121              
122             # If no specific exceptions are given, load all exceptions
123 14 50       90 @exceptions = @exceptions ? @exceptions : @all_exceptions;
124              
125 14         68 for my $exception (@exceptions) {
126             # Construct the module name: WebService::Hydra::Exception::ExceptionName
127 182         439 my $module_name = "WebService::Hydra::Exception::$exception";
128              
129 182 50       298 eval {
130 182         551 load $module_name; # Load the exception module
131 182         10621 1;
132             } or warn "Failed to load module $module_name: $@";
133             }
134             }
135              
136             1;