File Coverage

blib/lib/WebService/Fake.pm
Criterion Covered Total %
statement 109 133 81.9
branch 21 32 65.6
condition 23 50 46.0
subroutine 18 22 81.8
pod 6 6 100.0
total 177 243 72.8


line stmt bran cond sub pod time code
1             package WebService::Fake;
2              
3 2     2   2927 use strict;
  2         4  
  2         93  
4             { our $VERSION = '0.005'; }
5              
6 2     2   540 use Mojo::Base 'Mojolicious';
  2         194199  
  2         13  
7 2     2   410625 use Log::Any qw< $log >;
  2         17627  
  2         11  
8 2     2   5534 use YAML::XS qw< LoadFile >;
  2         5730  
  2         113  
9 2     2   1137 use Try::Tiny;
  2         2621  
  2         122  
10 2     2   16 use Scalar::Util qw< blessed >;
  2         6  
  2         84  
11 2     2   1160 use Template::Perlish;
  2         11448  
  2         10  
12 2     2   99 use 5.010;
  2         9  
13              
14             sub load_config {
15 1     1 1 2 my $config;
16             try {
17 1   50 1   61 my $config_file = $ENV{WEBSERVICE_FAKE} // 'webservice-fake.yml';
18 1         5 $config = LoadFile($config_file);
19              
20 1         417 my $custom = delete $config->{custom};
21 1 50 33     6 if ($custom && !blessed($custom)) {
22 0 0       0 $custom = {class => $custom} unless ref $custom;
23 0         0 local @INC = @INC;
24 0   0     0 unshift @INC, @{$custom->{include} // []};
  0         0  
25 0         0 (my $path = "$custom->{class}.pm") =~ s{::}{/}gmxs;
26 0         0 require $path;
27             } ## end if ($custom && !blessed...)
28 1 50       3 $config->{custom} = $custom->new($config)
29             if defined $custom;
30              
31 1   50     9 $config->{defaults}{template_start} //= '[%';
32 1   50     17 $config->{defaults}{template_stop} //= '%]';
33 1   50     6 $config->{defaults}{code} //= 200;
34 1   50     5 $config->{v} //= {};
35             } ## end try
36             catch {
37 0     0   0 my $msg = $_;
38 0 0       0 if (ref $_) {
39 0         0 require Data::Dumper;
40 0         0 local $Data::Dumper::Indent = 1;
41 0         0 $msg = Data::Dumper::Dumper($_);
42             }
43 0         0 $log->error($msg);
44 0         0 die $_;
45 1         9 };
46 1         31 return $config;
47             } ## end sub load_config
48              
49             sub startup {
50 1     1 1 24363 my $self = shift;
51              
52 1         6 my $config = $self->load_config;
53 1     0   10 $self->helper(config => sub { $config });
  0         0  
54 1   50     68 $self->secrets($config->{secrets} // ['Fake off!']);
55              
56 1         8 my $r = $self->routes;
57 1         6 for my $spec (@{$config->{routes}}) {
  1         4  
58 8         487 my $route = $r->any($spec->{path});
59             my @methods =
60 1         4 exists($spec->{methods}) ? @{$spec->{methods}}
61             : exists($spec->{method}) ? $spec->{method}
62 8 100       3538 : ();
    100          
63 8 100       33 $route->methods(map { uc($_) } @methods) if @methods;
  7         40  
64 8         112 $route->to(cb => $self->callback($spec, $config));
65             } ## end for my $spec (@{$config...})
66              
67 1         57 return $self;
68             } ## end sub startup
69              
70             sub callback {
71 8     8 1 23 my ($self, $spec, $config) = @_;
72 8         22 my $defaults = $config->{defaults};
73              
74 8         27 my $body_expander = $self->body_expander($spec, $config);
75 8         38 my $headers_expander = $self->headers_expander($spec, $config);
76 8         43 my $body_wrapper = $self->body_wrapper($spec, $config);
77              
78             return sub {
79 12     12   128250 my $c = shift;
80              
81 12         41 my $variables = {
82             body_params => $c->req->body_params->to_hash,
83             controller => $c,
84             headers => $c->req->headers->to_hash,
85             params => $c->req->params->to_hash,
86             query_params => $c->req->query_params->to_hash,
87             stash => scalar($c->stash()),
88             };
89 12         3528 $log->debug($c->req->to_string());
90              
91             # body, with exception handling for empty one, and wrapping
92 12         6246 my $body = $body_expander->($variables);
93 12 100       7720 if (!length $body) {
94 2 50       12 if ($spec->{on_empty}) {
95 0         0 my $r = $c->match()->root();
96 0         0 my $match = Mojolicious::Routes::Match->new(root => $r);
97 0         0 $match->match($c => $spec->{on_empty});
98 0         0 my $frame = $match->stack()->[0];
99 0         0 $c->stash($_ => $frame->{$_}) for keys %$frame;
100 0         0 return $frame->{cb}->($c);
101             } ## end if ($spec->{on_empty})
102             return $c->render_not_found()
103 2 50       8 if $spec->{not_found_on_empty};
104             } ## end if (!length $body)
105 12 100       170 $body = $body_wrapper->({%$variables, content => $body})
106             if $body_wrapper;
107              
108             # headers
109 12         1997 my $headers = $headers_expander->($variables);
110              
111 12         2354 my $response = $c->res;
112 12         188 $response->body($body);
113 12         491 my $rhs = $response->headers();
114 12         127 $rhs->header($_, @{$headers->{$_}}) for keys %$headers;
  38         676  
115 12         272 $response->fix_headers();
116              
117 12   66     1719 $c->rendered($spec->{code} // $defaults->{code});
118 8         8419 };
119              
120             } ## end sub callback
121              
122             sub headers_expander {
123 8     8 1 25 my ($self, $spec, $config) = @_;
124 8         29 my $defaults = $config->{defaults};
125              
126 8   33     46 my $start = $spec->{template_start} // $defaults->{template_start};
127 8   33     39 my $stop = $spec->{template_stop} // $defaults->{template_stop};
128              
129 8         17 my %hef;
130 8         13 for my $hs (
131 8   50     32 @{$defaults->{headers} // []}, # take them
132 8   100     42 @{$spec->{headers} // []}, # all
133             )
134             {
135 12         47 for my $name (keys %$hs) {
136 20         51 my $template = $hs->{$name};
137             my $expander = Template::Perlish->new(
138             start => $start,
139             stop => $stop,
140             variables => {
141             spec => $spec,
142             config => $config,
143             v => $config->{v},
144             }
145 20         137 )->compile_as_sub($template);
146 20   50     18969 push @{$hef{$name} //= []}, $expander;
  20         178  
147             } ## end for my $name (keys %$hs)
148             } ## end for my $hs (@{$defaults...})
149              
150             # Ensure there will be a Content-Type
151             $hef{'Content-Type'} //=
152 8   100 9   67 [sub { return 'application/json' }];
  9         45  
153              
154             return sub {
155 12     12   31 my ($variables) = @_;
156             return {
157             map {
158 12         52 $_ => [map { $_->($variables) } @{$hef{$_}}];
  38         7259  
  38         655  
  38         100  
159             } keys %hef
160             };
161 8         53 };
162             } ## end sub headers_expander
163              
164             sub body_expander {
165 8     8 1 24 my ($self, $spec, $config) = @_;
166 8         21 my $defaults = $config->{defaults};
167              
168 8   50     33 my $body = $spec->{body} // '[%%]';
169 8   33     48 my $start = $spec->{template_start} // $defaults->{template_start};
170 8   33     32 my $stop = $spec->{template_stop} // $defaults->{template_stop};
171              
172             my $be = Template::Perlish->new(
173             start => $start,
174             stop => $stop,
175             variables => {
176             spec => $spec,
177             config => $config,
178             v => $config->{v},
179             }
180 8         64 )->compile_as_sub($body);
181              
182 8   50     9436 my $trim = $spec->{trim} //= '';
183             return sub {
184 0     0   0 (my $body = $be->(@_)) =~ s{^\s+|\s+$}{}gmxs;
185 0         0 return $body;
186             }
187 8 50       34 if $trim eq 'lines';
188             return sub {
189 0     0   0 (my $body = $be->(@_)) =~ s{\A\s+|\s+\z}{}gmxs;
190 0         0 return $body;
191             }
192 8 50       24 if $trim eq 'ends';
193 8         23 return $be;
194             } ## end sub body_expander
195              
196             sub body_wrapper {
197 8     8 1 27 my ($self, $spec, $config) = @_;
198 8         23 my $defaults = $config->{defaults};
199              
200             my $wrapper =
201             exists($spec->{body_wrapper}) ? $spec->{body_wrapper}
202             : exists($defaults->{body_wrapper}) ? $defaults->{body_wrapper}
203 8 50       41 : undef;
    100          
204 8 100       35 return unless defined $wrapper;
205              
206 4   33     21 my $start = $spec->{template_start} // $defaults->{template_start};
207 4   33     19 my $stop = $spec->{template_stop} // $defaults->{template_stop};
208             return Template::Perlish->new(
209             start => $start,
210             stop => $stop,
211             variables => {
212             spec => $spec,
213             config => $config,
214             v => $config->{v},
215             }
216 4         25 )->compile_as_sub($wrapper);
217             } ## end sub body_wrapper
218              
219             1;
220             __END__