File Coverage

blib/lib/Plack/Middleware/HealthCheck.pm
Criterion Covered Total %
statement 75 75 100.0
branch 28 28 100.0
condition 10 10 100.0
subroutine 16 16 100.0
pod 5 5 100.0
total 134 134 100.0


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