File Coverage

blib/lib/Mojolicious/Plugin/OpenAPI/Modern.pm
Criterion Covered Total %
statement 95 98 96.9
branch 8 12 66.6
condition 10 12 83.3
subroutine 23 23 100.0
pod 1 1 100.0
total 137 146 93.8


line stmt bran cond sub pod time code
1 2     2   344795 use strictures 2;
  2         22  
  2         93  
2             package Mojolicious::Plugin::OpenAPI::Modern; # git description: v0.018-4-gf47457e
3             # vim: set ts=8 sts=2 sw=2 tw=100 et :
4             # ABSTRACT: Mojolicious plugin providing access to an OpenAPI document and parser
5             # KEYWORDS: validation evaluation JSON Schema OpenAPI Swagger HTTP request response
6              
7             our $VERSION = '0.019';
8              
9 2     2   1017 use 5.020;
  2         7  
10 2     2   526 use utf8;
  2         248  
  2         15  
11 2     2   110 use if "$]" >= 5.022, experimental => 're_strict';
  2         4  
  2         68  
12 2     2   124 no if "$]" >= 5.031009, feature => 'indirect';
  2         24  
  2         428  
13 2     2   14 no if "$]" >= 5.033001, feature => 'multidimensional';
  2         4  
  2         137  
14 2     2   11 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  2         4  
  2         146  
15 2     2   19 no if "$]" >= 5.041009, feature => 'smartmatch';
  2         4  
  2         94  
16 2     2   8 no feature 'switch';
  2         2  
  2         64  
17 2     2   928 use Mojo::Base 'Mojolicious::Plugin', -signatures;
  2         18378  
  2         13  
18 2     2   3772 use Feature::Compat::Try;
  2         766  
  2         11  
19 2     2   1558 use YAML::PP;
  2         137491  
  2         123  
20 2     2   1811 use Path::Tiny;
  2         31161  
  2         215  
21 2     2   1277 use Mojo::JSON 'decode_json';
  2         469536  
  2         182  
22 2     2   1103 use Safe::Isa;
  2         1396  
  2         364  
23 2     2   1856 use OpenAPI::Modern 0.100;
  2         1815530  
  2         100  
24 2     2   19 use namespace::clean;
  2         5  
  2         16  
25              
26             # we store data in two places: on the app (persistent storage, for the OpenAPI::Modern object
27             # itself) and in the controller stash: per-request data like the path info and extracted path items.
28              
29             # the first is $app->openapi or $c->openapi
30             # the second is $c->stash('openapi') which will be initialized to {} on first use.
31 5     5 1 2373652 sub register ($self, $app, $config) {
  5         13  
  5         8  
  5         8  
  5         10  
32 5         16 my $stash = Mojo::Util::_stash(openapi => $app);
33              
34 5         64 try {
35 5   66     33 my $openapi = $config->{openapi_obj} // OpenAPI::Modern->new(_process_configs($config));
36              
37             # leave room for other keys in our localized stash
38 5         3402073 $stash->{openapi} = $openapi;
39             }
40             catch ($e) {
41 0         0 die 'Cannot load OpenAPI document: ', $e;
42             }
43              
44 5     15   78 $app->helper(openapi => sub ($) { $stash->{openapi} });
  15         268  
  15         5950  
  15         32  
45              
46 5         548 $app->helper(validate_request => \&_validate_request);
47 5         298 $app->helper(validate_response => \&_validate_response);
48              
49 6     6   488446 $app->hook(after_dispatch => sub ($c) {
  6         18  
  6         25  
50 6         29 $c->res->on(finish => sub ($res) { $config->{after_response}->($c) });
  6         62  
51 5 50       301 }) if $config->{after_response};
52             }
53              
54             # converts a config hash into values suitable for constructing an OpenAPI::Modern object
55 4     4   9 sub _process_configs ($config) {
  4         6  
  4         6  
56 4         9 my $schema;
57 4 100       18 if (exists $config->{schema}) {
    50          
58 2         5 $schema = $config->{schema};
59             }
60             elsif (exists $config->{document_filename}) {
61 2 100       11 if ($config->{document_filename} =~ /\.ya?ml$/) {
    50          
62 1         18 $schema = YAML::PP->new(boolean => 'JSON::PP')->load_file($config->{document_filename}),
63             }
64             elsif ($config->{document_filename} =~ /\.json$/) {
65 1         16 $schema = decode_json(path($config->{document_filename})->slurp_raw);
66             }
67             else {
68 0         0 die 'Unsupported file format in filename: ', $config->{document_filename};
69             }
70             }
71             else {
72 0         0 die 'missing config: one of schema, filename';
73             }
74              
75 4         8319 my %config_copy = %$config;
76 4         19 delete @config_copy{qw(schema document_filename document_uri after_response)};
77 4 50       14 warn 'unrecognized config option(s): ', join(', ', keys %config_copy) if keys %config_copy;
78              
79             return {
80 4   100     119 openapi_uri => $config->{document_uri} // $config->{document_filename} // '',
      100        
81             openapi_schema => $schema,
82             };
83             }
84              
85 5     5   1597410 sub _validate_request ($c) {
  5         12  
  5         14  
86 5   50     19 my $options = $c->stash->{openapi} //= {};
87 5         104 return $c->openapi->validate_request($c->req, $options);
88             }
89              
90 6     6   268 sub _validate_response ($c) {
  6         15  
  6         41  
91 6   100     30 my $options = $c->stash->{openapi} //= {};
92 6         107 local $options->{request} = $c->req;
93 6         111 return $c->openapi->validate_response($c->res, $options);
94             }
95              
96             1;
97              
98             __END__