File Coverage

blib/lib/Plack/App/GitHub/WebHook.pm
Criterion Covered Total %
statement 95 99 95.9
branch 22 22 100.0
condition 15 19 78.9
subroutine 24 28 85.7
pod 2 4 50.0
total 158 172 91.8


line stmt bran cond sub pod time code
1             package Plack::App::GitHub::WebHook;
2 5     5   296522 use strict;
  5         12  
  5         162  
3 5     5   23 use warnings;
  5         7  
  5         120  
4 5     5   52 use v5.10;
  5         16  
  5         186  
5              
6 5     5   22 use parent 'Plack::Component';
  5         7  
  5         25  
7 5     5   17934 use Plack::Util::Accessor qw(hook access app events safe);
  5         1039  
  5         27  
8 5     5   2599 use Plack::Request;
  5         122640  
  5         160  
9 5     5   2015 use Plack::Builder;
  5         15000  
  5         347  
10 5     5   2065 use Plack::Middleware::Access;
  5         183508  
  5         225  
11 5     5   44 use Carp qw(croak);
  5         7  
  5         309  
12 5     5   3453 use JSON qw(decode_json);
  5         46719  
  5         21  
13              
14             our $VERSION = '0.7';
15              
16             sub prepare_app {
17 15     15 1 37705 my $self = shift;
18              
19 15 100 50     50 if ( (ref $self->hook // '') ne 'ARRAY' ) {
20 8   66     192 $self->hook( [ $self->hook // () ] );
21             }
22              
23 15         122 foreach my $task (@{$self->hook}) {
  15         26  
24 14 100 50     89 if ( (ref $task // '') ne 'CODE') {
25 1         155 croak "hook must be a CODE or ARRAY of CODEs";
26             }
27             }
28              
29             $self->access([
30 14 100       38 allow => "204.232.175.64/27",
31             allow => "192.30.252.0/22",
32             deny => "all"
33             ]) unless $self->access;
34              
35             $self->app( builder {
36 14     14   386 enable 'Access', rules => $self->access;
37 14         470 enable 'HTTPExceptions';
38 13         2018 sub { $self->call_granted($_[0]) },
39 14         153 } );
  14         4489  
40             }
41              
42             sub call {
43 15     15 1 20765 my ($self, $env) = @_;
44 15         42 $self->app->($env);
45             }
46              
47             sub call_granted {
48 13     13 0 17 my ($self, $env) = @_;
49              
50 13 100       36 if ( $env->{REQUEST_METHOD} ne 'POST' ) {
51 1         5 return [405,['Content-Type'=>'text/plain','Content-Length'=>18],['Method Not Allowed']];
52             }
53              
54 12         55 my $req = Plack::Request->new($env);
55 12   100     116 my $event = $env->{'HTTP_X_GITHUB_EVENT'} // '';
56 12   100     32 my $delivery = $env->{'HTTP_X_GITHUB_DELIVERY'} // '';
57 12         15 my $payload;
58 12         7 my ($status, $message);
59            
60 12 100 100     47 if ( !$self->events or grep { $event eq $_ } @{$self->events} ) {
  2         11  
  2         10  
61 11         92 $payload = eval { decode_json $req->content };
  11         30  
62             }
63              
64 12 100       3539 if (!$payload) {
65 2         14 return [400,['Content-Type'=>'text/plain','Content-Length'=>11],['Bad Request']];
66             }
67            
68             my $logger = Plack::App::GitHub::WebHook::Logger->new(
69 11     11   10 $env->{'psgix.logger'} || sub { }
70 10   100     127 );
71              
72 10 100       58 if ( $self->receive( [ $payload, $event, $delivery, $logger ], $env->{'psgi.errors'} ) ) {
73 6         9 ($status, $message) = (200,"OK");
74             } else {
75 3         6 ($status, $message) = (202,"Accepted");
76             }
77              
78 9 100       27 $message = ucfirst($event)." $message" if $self->events;
79              
80             return [
81 9         92 $status,
82             [ 'Content-Type' => 'text/plain', 'Content-Length' => length $message ],
83             [ $message ]
84             ];
85             }
86              
87             sub receive {
88 10     10 0 23 my ($self, $args, $error) = @_;
89              
90 10         11 foreach my $hook (@{$self->{hook}}) {
  10         28  
91 9 100 66     27 if ( !eval { $hook->(@$args) } || $@ ) {
  9         23  
92 3 100       20 if ( $@ ) {
93 2 100       8 if ($self->safe) {
94 1         14 $error->print($@);
95             } else {
96 1         47 die Plack::App::GitHub::WebHook::Exception->new( 500, $@ );
97             }
98             }
99 2         18 return;
100             }
101             }
102              
103 7         29 return scalar @{$self->{hook}};
  7         20  
104             }
105              
106             {
107             package Plack::App::GitHub::WebHook::Logger;
108             sub new {
109 10     10   29 my $self = bless { logger => $_[1] }, $_[0];
110 10         21 foreach my $level (qw(debug info warn error fatal)) {
111 10     10   38 $self->{$level} = sub { $self->log( $level => $_[0] ) }
112 50         165 }
113 10         17 $self;
114             }
115             sub log {
116 22     22   35 my ($self, $level, $message) = @_;
117 22         24 chomp $message;
118 22         54 $self->{logger}->({ level => $level, message => $message });
119 22         56 1;
120             }
121 0     0   0 sub debug { $_[0]->log(debug => $_[1]) }
122 0     0   0 sub info { $_[0]->log(info => $_[1]) }
123 0     0   0 sub warn { $_[0]->log(warn => $_[1]) }
124 0     0   0 sub error { $_[0]->log(error => $_[1]) }
125 2     2   12 sub fatal { $_[0]->log(fatal => $_[1]) }
126             }
127              
128             {
129             package Plack::App::GitHub::WebHook::Exception;
130 5     5   3341 use overload '""' => sub { $_[0]->{message} };
  5     1   9  
  5         41  
  1         1090  
131 1     1   14 sub new { bless { code => $_[1], message => $_[2] }, $_[0]; }
132 1     1   75 sub code { $_[0]->{code} }
133             }
134              
135             1;
136             __END__