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   361 use Mojo::Base 'Mojolicious::Plugin';
  48         136  
  48         343  
3              
4 48     48   8442 use JSON::Validator::Util qw(is_bool schema_type);
  48         147  
  48         2731  
5 48     48   376 use Mojo::JSON qw(encode_json decode_json);
  48         150  
  48         80169  
6              
7             sub register {
8 59     59 1 845 my ($self, $app, $config) = @_;
9              
10             $app->helper('openapi.build_response_body' => $config->{renderer}
11 59   100     782 || \&_helper_build_response_body);
12 59         29598 $app->helper('openapi.build_schema_request' => \&_helper_build_schema_request);
13 59         27028 $app->helper('openapi.build_schema_response' => \&_helper_build_schema_response);
14 59         28635 $app->helper('openapi.coerce_request_parameters' => \&_helper_coerce_request_parameters);
15 59         29875 $app->helper('openapi.coerce_response_parameters' => \&_helper_coerce_response_parameters);
16 59         31477 $app->helper('openapi.parse_request_body' => \&_helper_parse_request_body);
17             }
18              
19             sub _bool {
20 19 100   19   162 return map { !is_bool($_) ? $_ : $_ ? 'true' : 'false' } @_;
  21 100       67  
21             }
22              
23             sub _helper_build_response_body {
24 171     171   3741 my $c = shift;
25 171 50       800 return $_[0]->slurp if UNIVERSAL::isa($_[0], 'Mojo::Asset');
26 171 50       570 $c->res->headers->content_type('application/json;charset=UTF-8')
27             unless $c->res->headers->content_type;
28 171         7584 return encode_json($_[0]);
29             }
30              
31             sub _helper_build_schema_request {
32 176     176   3990 my $c = shift;
33 176         603 my $req = $c->req;
34              
35 176         1853 $c->stash->{'openapi.evaluated_request_parameters'} = \my @evaluated;
36              
37             return {
38             body => sub {
39 53     53   25542 $evaluated[@evaluated] = $c->openapi->parse_request_body($_[1]);
40             },
41             formData => sub {
42 51     51   6055 my $name = shift;
43 51         146 my $value = $req->body_params->every_param($name);
44 51         2610 my $n = @$value;
45 51 100       163 return $evaluated[@evaluated] = {exists => 1, value => $n > 1 ? $value : $value->[0]}
    100          
46             if $n > 0;
47              
48 44         116 $value = $req->upload($name);
49 44   66     1087 return $evaluated[@evaluated] = {exists => !!$value, value => $value && $value->size};
50             },
51             header => sub {
52 38     38   5729 my $name = shift;
53 38         115 my $value = $req->headers->every_header($name);
54 38         612 return $evaluated[@evaluated] = {exists => !!@$value, value => $value};
55             },
56             path => sub {
57 27     27   13095 my $name = shift;
58 27         119 my $stash = $c->match->stack->[-1];
59 27         349 return $evaluated[@evaluated] = {exists => exists $stash->{$name}, value => $stash->{$name}};
60             },
61             query => sub {
62 186 100   186   39227 return $evaluated[@evaluated] = {exists => 1, value => $req->url->query->to_hash}
63             unless my $name = shift;
64 152         467 my $value = $req->url->query->every_param($name);
65 152         6928 my $n = @$value;
66 152 100       1014 return $evaluated[@evaluated] = {exists => !!$n, value => $n > 1 ? $value : $value->[0]};
67             },
68 176         4808 };
69             }
70              
71             sub _helper_build_schema_response {
72 137     137   2984 my $c = shift;
73 137         503 my $res = $c->res;
74              
75 137         1684 $c->stash->{'openapi.evaluated_response_parameters'} = \my @evaluated;
76              
77             return {
78             body => sub {
79 130     130   8891 my $res = $c->stash('openapi');
80 130         1561 return $evaluated[@evaluated]
81             = {accept => $c->req->headers->accept, exists => !!$res, value => $res};
82             },
83             header => sub {
84 21     21   2344 my $name = shift;
85 21         84 my $value = $res->headers->every_header($name);
86 21         363 return $evaluated[@evaluated] = {exists => !!@$value, value => $value};
87             },
88 137         2620 };
89             }
90              
91             sub _helper_coerce_request_parameters {
92 176     176   4979 my ($c, $evaluated) = @_;
93 176         846 my $output = $c->validation->output;
94 176         88510 my $req = $c->req;
95              
96 176         1921 for my $i (@$evaluated) {
97 355 100       1489 next unless $i->{valid};
98 139         626 $output->{$i->{name}} = $i->{value};
99 139 100       564 $c->stash(@$i{qw(name value)}) if $i->{in} eq 'path';
100 3         61 $req->headers->header($i->{name}, ref $i->{value} eq 'ARRAY' ? @{$i->{value}} : $i->{value})
101 139 100       930 if $i->{in} eq 'header';
    100          
102 139 100       1012 $req->url->query->merge(@$i{qw(name value)}) if $i->{in} eq 'query';
103 139 100       4835 $req->params->merge(@$i{qw(name value)}) if $i->{in} eq 'query';
104 139 100       3661 $req->params->merge(@$i{qw(name value)}) if $i->{in} eq 'formData';
105 139 100       1039 $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   2700 my ($c, $evaluated) = @_;
111 137         487 my $res = $c->res;
112              
113 137         1595 for my $i (@$evaluated) {
114 151 100       1170 next unless $i->{valid};
115 137 100       774 $c->stash(openapi_negotiated_content_type => $i->{content_type}) if $i->{in} eq 'body';
116             $res->headers->header($i->{name},
117 6         75 _bool(ref $i->{value} eq 'ARRAY' ? @{$i->{value}} : $i->{value}))
118 137 100       2698 if $i->{in} eq 'header';
    100          
119             }
120             }
121              
122             sub _helper_parse_request_body {
123 53     53   1040 my ($c, $param) = @_;
124              
125 53   100     214 my $content_type = $c->req->headers->content_type || '';
126 53         1682 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     191 if grep { $content_type eq $_ } qw(application/x-www-form-urlencoded multipart/form-data);
  106         429  
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     954 $res->{value} //= $c->req->json // decode_json $c->req->body;
      100        
135 37         5346 1;
136 53 100       1826 } or do {
137 16         35116 $res->{value} = $c->req->body;
138             };
139              
140 53         752 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