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   303 use Mojo::Base 'Mojolicious::Plugin';
  48         97  
  48         289  
3              
4 48     48   7567 use JSON::Validator::Util qw(is_bool schema_type);
  48         119  
  48         2384  
5 48     48   285 use Mojo::JSON qw(encode_json decode_json);
  48         105  
  48         66298  
6              
7             sub register {
8 59     59 1 610 my ($self, $app, $config) = @_;
9              
10             $app->helper('openapi.build_response_body' => $config->{renderer}
11 59   100     607 || \&_helper_build_response_body);
12 59         23592 $app->helper('openapi.build_schema_request' => \&_helper_build_schema_request);
13 59         21047 $app->helper('openapi.build_schema_response' => \&_helper_build_schema_response);
14 59         22010 $app->helper('openapi.coerce_request_parameters' => \&_helper_coerce_request_parameters);
15 59         23617 $app->helper('openapi.coerce_response_parameters' => \&_helper_coerce_response_parameters);
16 59         25531 $app->helper('openapi.parse_request_body' => \&_helper_parse_request_body);
17             }
18              
19             sub _bool {
20 19 100   19   132 return map { !is_bool($_) ? $_ : $_ ? 'true' : 'false' } @_;
  21 100       49  
21             }
22              
23             sub _helper_build_response_body {
24 170     170   3119 my $c = shift;
25 170 50       797 return $_[0]->slurp if UNIVERSAL::isa($_[0], 'Mojo::Asset');
26 170 50       482 $c->res->headers->content_type('application/json;charset=UTF-8')
27             unless $c->res->headers->content_type;
28 170         6399 return encode_json($_[0]);
29             }
30              
31             sub _helper_build_schema_request {
32 174     174   3444 my $c = shift;
33 174         514 my $req = $c->req;
34              
35 174         1654 $c->stash->{'openapi.evaluated_request_parameters'} = \my @evaluated;
36              
37             return {
38             body => sub {
39 51     51   20470 $evaluated[@evaluated] = $c->openapi->parse_request_body($_[1]);
40             },
41             formData => sub {
42 51     51   4705 my $name = shift;
43 51         124 my $value = $req->body_params->every_param($name);
44 51         2200 my $n = @$value;
45 51 100       129 return $evaluated[@evaluated] = {exists => 1, value => $n > 1 ? $value : $value->[0]}
    100          
46             if $n > 0;
47              
48 44         100 $value = $req->upload($name);
49 44   66     856 return $evaluated[@evaluated] = {exists => !!$value, value => $value && $value->size};
50             },
51             header => sub {
52 38     38   4840 my $name = shift;
53 38         105 my $value = $req->headers->every_header($name);
54 38         547 return $evaluated[@evaluated] = {exists => !!@$value, value => $value};
55             },
56             path => sub {
57 27     27   11022 my $name = shift;
58 27         103 my $stash = $c->match->stack->[-1];
59 27         285 return $evaluated[@evaluated] = {exists => exists $stash->{$name}, value => $stash->{$name}};
60             },
61             query => sub {
62 186 100   186   31578 return $evaluated[@evaluated] = {exists => 1, value => $req->url->query->to_hash}
63             unless my $name = shift;
64 152         403 my $value = $req->url->query->every_param($name);
65 152         6092 my $n = @$value;
66 152 100       766 return $evaluated[@evaluated] = {exists => !!$n, value => $n > 1 ? $value : $value->[0]};
67             },
68 174         4426 };
69             }
70              
71             sub _helper_build_schema_response {
72 137     137   2509 my $c = shift;
73 137         452 my $res = $c->res;
74              
75 137         1411 $c->stash->{'openapi.evaluated_response_parameters'} = \my @evaluated;
76              
77             return {
78             body => sub {
79 130     130   7070 my $res = $c->stash('openapi');
80 130         1246 return $evaluated[@evaluated]
81             = {accept => $c->req->headers->accept, exists => !!$res, value => $res};
82             },
83             header => sub {
84 21     21   1954 my $name = shift;
85 21         68 my $value = $res->headers->every_header($name);
86 21         283 return $evaluated[@evaluated] = {exists => !!@$value, value => $value};
87             },
88 137         2166 };
89             }
90              
91             sub _helper_coerce_request_parameters {
92 174     174   4219 my ($c, $evaluated) = @_;
93 174         737 my $output = $c->validation->output;
94 174         75212 my $req = $c->req;
95              
96 174         1551 for my $i (@$evaluated) {
97 353 100       1252 next unless $i->{valid};
98 138         450 $output->{$i->{name}} = $i->{value};
99 138 100       497 $c->stash(@$i{qw(name value)}) if $i->{in} eq 'path';
100 3         33 $req->headers->header($i->{name}, ref $i->{value} eq 'ARRAY' ? @{$i->{value}} : $i->{value})
101 138 100       793 if $i->{in} eq 'header';
    100          
102 138 100       850 $req->url->query->merge(@$i{qw(name value)}) if $i->{in} eq 'query';
103 138 100       3845 $req->params->merge(@$i{qw(name value)}) if $i->{in} eq 'query';
104 138 100       2970 $req->params->merge(@$i{qw(name value)}) if $i->{in} eq 'formData';
105 138 100       886 $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   2211 my ($c, $evaluated) = @_;
111 137         379 my $res = $c->res;
112              
113 137         1378 for my $i (@$evaluated) {
114 151 100       984 next unless $i->{valid};
115 137 100       644 $c->stash(openapi_negotiated_content_type => $i->{content_type}) if $i->{in} eq 'body';
116             $res->headers->header($i->{name},
117 6         63 _bool(ref $i->{value} eq 'ARRAY' ? @{$i->{value}} : $i->{value}))
118 137 100       2214 if $i->{in} eq 'header';
    100          
119             }
120             }
121              
122             sub _helper_parse_request_body {
123 51     51   816 my ($c, $param) = @_;
124              
125 51   100     189 my $content_type = $c->req->headers->content_type || '';
126 51         1463 my $res = {content_type => $content_type, exists => !!$c->req->body_size};
127              
128             eval {
129             $res->{value} //= $c->req->body_params->to_hash
130 51 100 33     136 if grep { $content_type eq $_ } qw(application/x-www-form-urlencoded multipart/form-data);
  102         334  
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 51   100     769 $res->{value} //= $c->req->json // decode_json $c->req->body;
      100        
135 35         4884 1;
136 51 100       1731 } or do {
137 16         27878 $res->{value} = $c->req->body;
138             };
139              
140 51         622 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