File Coverage

blib/lib/Plack/Middleware/HealthCheck.pm
Criterion Covered Total %
statement 79 79 100.0
branch 35 36 97.2
condition 9 10 90.0
subroutine 16 16 100.0
pod 5 5 100.0
total 144 146 98.6


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 1     1   222639 use version;
  1         5  
  1         6  
5             our $VERSION = 'v0.0.4'; # VERSION
6              
7 1     1   123 use 5.010;
  1         4  
8 1     1   5 use strict;
  1         2  
  1         32  
9 1     1   7 use warnings;
  1         2  
  1         45  
10 1     1   6 use parent 'Plack::Middleware';
  1         3  
  1         5  
11 1     1   6570 use Plack::Request;
  1         38747  
  1         56  
12 1         21 use Plack::Util::Accessor qw(
13             health_check
14             health_check_paths
15             allowed_params
16 1     1   10 );
  1         3  
17              
18 1     1   89 use Carp;
  1         2  
  1         95  
19 1     1   10 use JSON ();
  1         3  
  1         17  
20 1     1   6 use Scalar::Util ();
  1         2  
  1         795  
21              
22             sub new {
23 20     20 1 21202 my ( $class, @args ) = @_;
24             my %params = @args == 1
25 20 100 100     138 && Scalar::Util::reftype $args[0] eq 'HASH' ? %{ $args[0] } : @args;
  5         26  
26              
27 20 100       83 if ( $params{health_check} ) {
28             croak "health_check doesn't seem like a HealthCheck"
29 18 100       44 unless do { local $@; eval { local $SIG{__DIE__};
  18         30  
  18         38  
  18         70  
30 18         150 $params{health_check}->can('check') } };
31             }
32             else {
33 2         26 croak "health_check parameter required";
34             }
35              
36             # Adding default params if none specified
37             $params{allowed_params} = [qw< runtime >]
38 17 100       57 unless exists $params{allowed_params};
39              
40             # custom query param filter validation
41 17         34 my $error = "HealthCheck allowed_params must be an arrayref of strings";
42 17         58 my $ref = Scalar::Util::reftype $params{allowed_params};
43              
44 17 100       57 if ( !$ref ) { # someone sent a scalar; massage it
    100          
45 1         3 $params{allowed_params} = [ $params{allowed_params} ];
46             }
47             elsif ( $ref ne 'ARRAY' ) {
48 1         16 croak "$error; found $ref";
49             }
50              
51 16         27 foreach my $param ( @{ $params{allowed_params} } ) {
  16         45  
52 17 100       69 if ( my $ref = Scalar::Util::reftype $param ) {
    100          
53 1         16 croak "$error; found $ref value";
54             }
55             elsif ( lc $param eq 'env' ) {
56 1         19 croak "Cannot overload \%env params";
57             }
58             }
59              
60 14         81 return $class->SUPER::new(
61             health_check_paths => ['/healthz'],
62             %params,
63             );
64             }
65              
66             sub call {
67 26     26 1 69912 my ( $self, $env ) = @_;
68              
69 26 100       67 return $self->serve_health_check($env)
70             if $self->should_serve_health_check($env);
71              
72 3         17 return $self->app->($env);
73             }
74              
75             sub should_serve_health_check {
76 41     41 1 5446 my ( $self, $env ) = @_;
77              
78 41         76 my $path = $env->{'PATH_INFO'};
79 41 100       71 foreach ( @{ $self->health_check_paths || [] } ) {
  41         115  
80 42 100       342 return 1 if $path eq $_;
81             }
82              
83 14         108 return 0;
84             }
85              
86             sub serve_health_check {
87 23     23 1 49 my ( $self, $env ) = @_;
88              
89 23         123 my $req = Plack::Request->new($env);
90 23         217 my $query_params = $req->query_parameters; # a Hash::MultiValue
91 23   50     2309 my $allowed_params = $self->allowed_params || []; # an array
92              
93 23         142 my %check_params = ( env => $env );
94              
95 23         43 foreach my $param ( @{$allowed_params}, 'tags' ) {
  23         52  
96 46 100       275 if( exists $query_params->{$param} ){
97             $check_params{$param} = [ $query_params->get_all($param) ]
98 21 50       65 if exists $query_params->{$param};
99             }
100             }
101              
102             # turn on runtime if pretty and make param value scalar not array
103 23 100       214 if ( exists $check_params{runtime} ) {
    100          
104             $check_params{runtime}
105 6 100       22 = $check_params{runtime}[0] eq '' ? 1 : $check_params{runtime}[0];
106             }
107             elsif ( exists $req->query_parameters->{pretty} ) {
108 1         10 $check_params{runtime} = 1;
109             }
110              
111 23     1   240 local $SIG{__WARN__} = sub { $env->{'psgi.errors'}->print($_) for @_ };
  1         187  
112 23         71 return $self->health_check_response(
113             $self->health_check->check(%check_params), $req );
114             }
115              
116             sub health_check_response {
117 31     31 1 3197 my ( $self, $result, $req ) = @_;
118 31         209 my $json = JSON->new->utf8;
119             $json->canonical->pretty
120 31 100 100     110 if $req and exists $req->query_parameters->{pretty};
121             return [
122 31 100 100     814 ( $result->{status} || '' ) eq 'OK' ? 200 : 503,
123             [ content_type => 'application/json; charset=utf-8' ],
124             [ $json->encode($result) ] ];
125             }
126              
127             1;
128              
129             __END__