File Coverage

blib/lib/Plack/Middleware/HealthCheck.pm
Criterion Covered Total %
statement 89 89 100.0
branch 40 42 95.2
condition 10 11 90.9
subroutine 18 18 100.0
pod 7 7 100.0
total 164 167 98.2


line stmt bran cond sub pod time code
1             package Plack::Middleware::HealthCheck;
2              
3             # ABSTRACT: A health check endpoint for your Plack app
4 2     2   583881 use version;
  2         5  
  2         16  
5             our $VERSION = 'v0.2.1'; # VERSION
6              
7 2     2   239 use 5.010;
  2         14  
8 2     2   13 use strict;
  2         4  
  2         82  
9 2     2   11 use warnings;
  2         4  
  2         125  
10 2     2   11 use parent 'Plack::Middleware';
  2         4  
  2         15  
11 2     2   26030 use Plack::Request;
  2         130133  
  2         112  
12 2         21 use Plack::Util::Accessor qw(
13             health_check
14             health_check_paths
15             allowed_params
16 2     2   21 );
  2         9  
17              
18 2     2   382 use Carp;
  2         7  
  2         121  
19 2     2   1186 use JSON ();
  2         11726  
  2         52  
20 2     2   14 use Scalar::Util ();
  2         4  
  2         4940  
21              
22             sub new {
23 22     22 1 278743 my ( $class, @args ) = @_;
24             my %params = @args == 1
25 22 100 100     176 && Scalar::Util::reftype $args[0] eq 'HASH' ? %{ $args[0] } : @args;
  5         25  
26              
27 22 100       99 if ( $params{health_check} ) {
28             croak "health_check doesn't seem like a HealthCheck"
29 20 100       38 unless do { local $@; eval { local $SIG{__DIE__};
  20         35  
  20         43  
  20         91  
30 20         216 $params{health_check}->can('check') } };
31             }
32             else {
33 2         31 croak "health_check parameter required";
34             }
35              
36             # Adding default params if none specified
37             $params{allowed_params} = [qw< runtime >]
38 19 100       113 unless exists $params{allowed_params};
39              
40             # custom query param filter validation
41 19         37 my $error = "HealthCheck allowed_params must be an arrayref of strings";
42 19         61 my $ref = Scalar::Util::reftype $params{allowed_params};
43              
44 19 100       76 if ( !$ref ) { # someone sent a scalar; massage it
    100          
45 1         4 $params{allowed_params} = [ $params{allowed_params} ];
46             }
47             elsif ( $ref ne 'ARRAY' ) {
48 1         20 croak "$error; found $ref";
49             }
50              
51 18         31 foreach my $param ( @{ $params{allowed_params} } ) {
  18         55  
52 19 100       97 if ( my $ref = Scalar::Util::reftype $param ) {
    100          
53 1         19 croak "$error; found $ref value";
54             }
55             elsif ( lc $param eq 'env' ) {
56 1         24 croak "Cannot overload \%env params";
57             }
58             }
59              
60 16         114 return $class->SUPER::new(
61             health_check_paths => ['/healthz'],
62             %params,
63             );
64             }
65              
66             sub call {
67 26     26 1 102203 my ( $self, $env ) = @_;
68              
69 26 100       83 return $self->serve_health_check($env)
70             if $self->should_serve_health_check($env);
71              
72 3 50       14 return $self->serve_tags_list($env)
73             if $self->should_serve_tags_list($env);
74              
75 3         19 return $self->app->($env);
76             }
77              
78             sub should_serve_health_check {
79 41     41 1 7122 my ( $self, $env ) = @_;
80              
81 41         93 my $path = $env->{'PATH_INFO'};
82 41 100       69 foreach ( @{ $self->health_check_paths || [] } ) {
  41         173  
83 42 100       440 return 1 if $path eq $_;
84             }
85              
86 14         131 return 0;
87             }
88              
89             sub should_serve_tags_list {
90 15     15 1 41 my ( $self, $env ) = @_;
91              
92 15         33 my $path = $env->{'PATH_INFO'};
93 15 100       27 foreach ( @{ $self->health_check_paths || [] } ) {
  15         50  
94 19 100       149 return 1 if $path eq "$_/tags";
95             }
96              
97 11         92 return 0;
98             }
99              
100             sub serve_health_check {
101 23     23 1 69 my ( $self, $env ) = @_;
102              
103 23         187 my $req = Plack::Request->new($env);
104 23         341 my $query_params = $req->query_parameters; # a Hash::MultiValue
105 23   50     3152 my $allowed_params = $self->allowed_params || []; # an array
106              
107 23         242 my %check_params = ( env => $env );
108              
109 23         43 foreach my $param ( @{$allowed_params}, 'tags' ) {
  23         58  
110 46 100       331 if( exists $query_params->{$param} ){
111             $check_params{$param} = [ $query_params->get_all($param) ]
112 21 50       81 if exists $query_params->{$param};
113             }
114             }
115              
116             # turn on runtime if pretty and make param value scalar not array
117 23 100       310 if ( exists $check_params{runtime} ) {
    100          
118             $check_params{runtime}
119 6 100       45 = $check_params{runtime}[0] eq '' ? 1 : $check_params{runtime}[0];
120             }
121             elsif ( exists $req->query_parameters->{pretty} ) {
122 1         10 $check_params{runtime} = 1;
123             }
124              
125 23     1   315 local $SIG{__WARN__} = sub { $env->{'psgi.errors'}->print($_) for @_ };
  1         232  
126 23         87 return $self->health_check_response(
127             $self->health_check->check(%check_params), $req );
128             }
129              
130             sub serve_tags_list {
131 4     4 1 83 my ( $self, $env ) = @_;
132              
133 4         22 my $req = Plack::Request->new($env);
134              
135 4         64 return $self->health_check_response(
136             [ $self->health_check->get_registered_tags ], $req );
137             }
138              
139             sub health_check_response {
140 37     37 1 7454 my ( $self, $result, $req ) = @_;
141 37         432 my $json = JSON->new->allow_blessed->convert_blessed->utf8;
142             $json->canonical->pretty
143 37 100 100     152 if $req and exists $req->query_parameters->{pretty};
144             return [
145 37 100 100     1973 ref $result eq 'ARRAY' || ( $result->{status} || '' ) eq 'OK'
146             ? 200 : 503,
147             [ 'Content-Type' => 'application/json; charset=utf-8' ],
148             [ $json->encode($result) ] ];
149             }
150              
151             1;
152              
153             __END__