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   226129 use version;
  1         5  
  1         7  
5             our $VERSION = 'v0.0.5'; # VERSION
6              
7 1     1   109 use 5.010;
  1         4  
8 1     1   5 use strict;
  1         2  
  1         20  
9 1     1   5 use warnings;
  1         2  
  1         36  
10 1     1   7 use parent 'Plack::Middleware';
  1         3  
  1         5  
11 1     1   6382 use Plack::Request;
  1         38564  
  1         42  
12 1         10 use Plack::Util::Accessor qw(
13             health_check
14             health_check_paths
15             allowed_params
16 1     1   9 );
  1         2  
17              
18 1     1   86 use Carp;
  1         2  
  1         74  
19 1     1   8 use JSON ();
  1         2  
  1         15  
20 1     1   5 use Scalar::Util ();
  1         3  
  1         960  
21              
22             sub new {
23 20     20 1 21387 my ( $class, @args ) = @_;
24             my %params = @args == 1
25 20 100 100     132 && Scalar::Util::reftype $args[0] eq 'HASH' ? %{ $args[0] } : @args;
  5         47  
26              
27 20 100       84 if ( $params{health_check} ) {
28             croak "health_check doesn't seem like a HealthCheck"
29 18 100       41 unless do { local $@; eval { local $SIG{__DIE__};
  18         31  
  18         39  
  18         64  
30 18         132 $params{health_check}->can('check') } };
31             }
32             else {
33 2         24 croak "health_check parameter required";
34             }
35              
36             # Adding default params if none specified
37             $params{allowed_params} = [qw< runtime >]
38 17 100       58 unless exists $params{allowed_params};
39              
40             # custom query param filter validation
41 17         33 my $error = "HealthCheck allowed_params must be an arrayref of strings";
42 17         54 my $ref = Scalar::Util::reftype $params{allowed_params};
43              
44 17 100       55 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         15 croak "$error; found $ref";
49             }
50              
51 16         29 foreach my $param ( @{ $params{allowed_params} } ) {
  16         36  
52 17 100       67 if ( my $ref = Scalar::Util::reftype $param ) {
    100          
53 1         14 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         74 return $class->SUPER::new(
61             health_check_paths => ['/healthz'],
62             %params,
63             );
64             }
65              
66             sub call {
67 26     26 1 69738 my ( $self, $env ) = @_;
68              
69 26 100       63 return $self->serve_health_check($env)
70             if $self->should_serve_health_check($env);
71              
72 3         14 return $self->app->($env);
73             }
74              
75             sub should_serve_health_check {
76 41     41 1 5563 my ( $self, $env ) = @_;
77              
78 41         82 my $path = $env->{'PATH_INFO'};
79 41 100       69 foreach ( @{ $self->health_check_paths || [] } ) {
  41         115  
80 42 100       340 return 1 if $path eq $_;
81             }
82              
83 14         104 return 0;
84             }
85              
86             sub serve_health_check {
87 23     23 1 45 my ( $self, $env ) = @_;
88              
89 23         120 my $req = Plack::Request->new($env);
90 23         208 my $query_params = $req->query_parameters; # a Hash::MultiValue
91 23   50     2310 my $allowed_params = $self->allowed_params || []; # an array
92              
93 23         145 my %check_params = ( env => $env );
94              
95 23         33 foreach my $param ( @{$allowed_params}, 'tags' ) {
  23         50  
96 46 100       272 if( exists $query_params->{$param} ){
97             $check_params{$param} = [ $query_params->get_all($param) ]
98 21 50       63 if exists $query_params->{$param};
99             }
100             }
101              
102             # turn on runtime if pretty and make param value scalar not array
103 23 100       209 if ( exists $check_params{runtime} ) {
    100          
104             $check_params{runtime}
105 6 100       19 = $check_params{runtime}[0] eq '' ? 1 : $check_params{runtime}[0];
106             }
107             elsif ( exists $req->query_parameters->{pretty} ) {
108 1         11 $check_params{runtime} = 1;
109             }
110              
111 23     1   239 local $SIG{__WARN__} = sub { $env->{'psgi.errors'}->print($_) for @_ };
  1         172  
112 23         68 return $self->health_check_response(
113             $self->health_check->check(%check_params), $req );
114             }
115              
116             sub health_check_response {
117 31     31 1 3209 my ( $self, $result, $req ) = @_;
118 31         202 my $json = JSON->new->utf8;
119             $json->canonical->pretty
120 31 100 100     122 if $req and exists $req->query_parameters->{pretty};
121             return [
122 31 100 100     868 ( $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__