File Coverage

blib/lib/Mojolicious/Plugin/OpenAPI/Parameters.pm
Criterion Covered Total %
statement 82 82 100.0
branch 42 44 95.4
condition 13 16 81.2
subroutine 18 18 100.0
pod 1 1 100.0
total 156 161 96.8


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::OpenAPI::Parameters;
2 48     48   346 use Mojo::Base 'Mojolicious::Plugin';
  48         107  
  48         434  
3              
4 48     48   15067 use JSON::Validator::Util qw(is_bool schema_type);
  48         118  
  48         3727  
5 48     48   384 use Mojo::JSON qw(encode_json decode_json);
  48         122  
  48         108648  
6              
7             sub register {
8 59     59 1 680 my ($self, $app, $config) = @_;
9              
10             $app->helper('openapi.build_response_body' => $config->{renderer}
11 59   100     686 || \&_helper_build_response_body);
12 59         34041 $app->helper('openapi.build_schema_request' => \&_helper_build_schema_request);
13 59         30596 $app->helper('openapi.build_schema_response' => \&_helper_build_schema_response);
14 59         31827 $app->helper('openapi.coerce_request_parameters' => \&_helper_coerce_request_parameters);
15 59         36441 $app->helper('openapi.coerce_response_parameters' => \&_helper_coerce_response_parameters);
16 59         34759 $app->helper('openapi.parse_request_body' => \&_helper_parse_request_body);
17             }
18              
19             sub _bool {
20 19 100   19   160 return map { !is_bool($_) ? $_ : $_ ? 'true' : 'false' } @_;
  21 100       61  
21             }
22              
23             sub _helper_build_response_body {
24 171     171   4885 my $c = shift;
25 171 50       969 return $_[0]->slurp if UNIVERSAL::isa($_[0], 'Mojo::Asset');
26 171 50       736 $c->res->headers->content_type('application/json;charset=UTF-8')
27             unless $c->res->headers->content_type;
28 171         9505 return encode_json($_[0]);
29             }
30              
31             sub _helper_build_schema_request {
32 176     176   5202 my $c = shift;
33 176         780 my $req = $c->req;
34              
35 176         2314 $c->stash->{'openapi.evaluated_request_parameters'} = \my @evaluated;
36              
37             return {
38             body => sub {
39 53     53   30375 $evaluated[@evaluated] = $c->openapi->parse_request_body($_[1]);
40             },
41             formData => sub {
42 51     51   12251 my $name = shift;
43 51         164 my $value = $req->body_params->every_param($name);
44 51         8259 my $n = @$value;
45 51 100       169 return $evaluated[@evaluated] = {exists => 1, value => $n > 1 ? $value : $value->[0]}
    100          
46             if $n > 0;
47              
48 44         137 $value = $req->upload($name);
49 44   66     1149 return $evaluated[@evaluated] = {exists => !!$value, value => $value && $value->size};
50             },
51             header => sub {
52 38     38   6497 my $name = shift;
53 38         119 my $value = $req->headers->every_header($name);
54 38         594 return $evaluated[@evaluated] = {exists => !!@$value, value => $value};
55             },
56             path => sub {
57 27     27   15886 my $name = shift;
58 27         159 my $stash = $c->match->stack->[-1];
59 27         441 return $evaluated[@evaluated] = {exists => exists $stash->{$name}, value => $stash->{$name}};
60             },
61             query => sub {
62 186 100   186   49731 return $evaluated[@evaluated] = {exists => 1, value => $req->url->query->to_hash}
63             unless my $name = shift;
64 152         638 my $value = $req->url->query->every_param($name);
65 152         7751 my $n = @$value;
66 152 100       1084 return $evaluated[@evaluated] = {exists => !!$n, value => $n > 1 ? $value : $value->[0]};
67             },
68 176         7095 };
69             }
70              
71             sub _helper_build_schema_response {
72 137     137   3911 my $c = shift;
73 137         554 my $res = $c->res;
74              
75 137         1928 $c->stash->{'openapi.evaluated_response_parameters'} = \my @evaluated;
76              
77             return {
78             body => sub {
79 131     131   11363 my $res = $c->stash('openapi');
80 131         1738 return $evaluated[@evaluated]
81             = {accept => $c->req->headers->accept, exists => !!$res, value => $res};
82             },
83             header => sub {
84 21     21   2296 my $name = shift;
85 21         67 my $value = $res->headers->every_header($name);
86 21         307 return $evaluated[@evaluated] = {exists => !!@$value, value => $value};
87             },
88 137         3306 };
89             }
90              
91             sub _helper_coerce_request_parameters {
92 176     176   6634 my ($c, $evaluated) = @_;
93 176         1358 my $output = $c->validation->output;
94 176         114521 my $req = $c->req;
95              
96 176         2225 for my $i (@$evaluated) {
97 355 100       1676 next unless $i->{valid};
98 139         701 $output->{$i->{name}} = $i->{value};
99 139 100       655 $c->stash(@$i{qw(name value)}) if $i->{in} eq 'path';
100 3         49 $req->headers->header($i->{name}, ref $i->{value} eq 'ARRAY' ? @{$i->{value}} : $i->{value})
101 139 100       1083 if $i->{in} eq 'header';
    100          
102 139 100       1252 $req->url->query->merge(@$i{qw(name value)}) if $i->{in} eq 'query';
103 139 100       5326 $req->params->merge(@$i{qw(name value)}) if $i->{in} eq 'query';
104 139 100       3906 $req->params->merge(@$i{qw(name value)}) if $i->{in} eq 'formData';
105 139 100       1366 $req->body_params->merge(@$i{qw(name value)}) if $i->{in} eq 'formData';
106             }
107             }
108              
109             sub _helper_coerce_response_parameters {
110 137     137   3745 my ($c, $evaluated) = @_;
111 137         626 my $res = $c->res;
112              
113 137         1975 for my $i (@$evaluated) {
114 152 100       1326 next unless $i->{valid};
115 138 100       855 $c->stash(openapi_negotiated_content_type => $i->{content_type}) if $i->{in} eq 'body';
116             $res->headers->header($i->{name},
117 6         88 _bool(ref $i->{value} eq 'ARRAY' ? @{$i->{value}} : $i->{value}))
118 138 100       3253 if $i->{in} eq 'header';
    100          
119             }
120             }
121              
122             sub _helper_parse_request_body {
123 53     53   1268 my ($c, $param) = @_;
124              
125 53   100     275 my $content_type = $c->req->headers->content_type || '';
126 53         2541 my $res = {content_type => $content_type, exists => !!$c->req->body_size};
127              
128             eval {
129             $res->{value} //= $c->req->body_params->to_hash
130 53 100 33     174 if grep { $content_type eq $_ } qw(application/x-www-form-urlencoded multipart/form-data);
  106         442  
131              
132             # Trying to use the already parsed json() or fallback to manually decoding the request
133             # since it will make the eval {} fail on invalid json.
134 53   100     996 $res->{value} //= $c->req->json // decode_json $c->req->body;
      100        
135 37         2827 1;
136 53 100       2441 } or do {
137 16         22392 $res->{value} = $c->req->body;
138             };
139              
140 53         945 return $res;
141             }
142              
143             1;
144              
145             =encoding utf8
146              
147             =head1 NAME
148              
149             Mojolicious::Plugin::OpenAPI::Parameters - Methods for transforming data from/to JSON::Validator::Schema
150              
151             =head1 DESCRIPTION
152              
153             L adds helpers to your L
154             application, required by L. These helpers can be
155             redefined in case you have special needs.
156              
157             =head1 HELPERS
158              
159             =head2 openapi.build_response_body
160              
161             $bytes = $c->openapi->build_response_body(Mojo::Asset->new);
162             $bytes = $c->openapi->build_response_body($data);
163              
164             Takes validated data and turns it into bytes that will be used as HTTP response
165             body. This method is useful to override, in case you want to render some other
166             structure than JSON.
167              
168             =head2 openapi.build_schema_request
169              
170             $hash_ref = $c->openapi->build_schema_request;
171              
172             Builds input data for L.
173              
174             =head2 openapi.build_schema_response
175              
176             $hash_ref = $c->openapi->build_schema_response;
177              
178             Builds input data for L.
179              
180             =head2 openapi.coerce_request_parameters
181              
182             $c->openapi->coerce_request_parameters(\@evaluated_parameters);
183              
184             Used by L to write the validated data back to
185             L and
186             L.
187              
188             =head2 openapi.coerce_response_parameters
189              
190             $c->openapi->coerce_response_parameters(\@evaluated_parameters);
191              
192             Used by L to write the validated data to
193             L.
194              
195             =head2 openapi.parse_request_body
196              
197             $hash_ref = $c->openapi->parse_request_body;
198              
199             Returns a structure representing the request body. The default is to parse the
200             input as JSON:
201              
202             {content_type => "application/json", exists => !!$c->req->body_size, value => $c->req->json};
203              
204             This method is useful to override, in case you want to parse some other
205             structure than JSON.
206              
207             =head1 METHODS
208              
209             =head2 register
210              
211             $self->register($app, \%config);
212              
213             This method will add the L to your L C<$app>.
214              
215             =head1 SEE ALSO
216              
217             L.
218              
219             =cut