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   55240 use strictures 2;
  2         10  
  2         57  
2             package Mojolicious::Plugin::OpenAPI::Modern; # git description: v0.020-2-g5da4c0d
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.021';
8              
9 2     2   699 use 5.020;
  2         7  
10 2     2   388 use utf8;
  2         198  
  2         10  
11 2     2   81 use if "$]" >= 5.022, experimental => 're_strict';
  2         2  
  2         37  
12 2     2   100 no if "$]" >= 5.031009, feature => 'indirect';
  2         3  
  2         204  
13 2     2   8 no if "$]" >= 5.033001, feature => 'multidimensional';
  2         3  
  2         68  
14 2     2   7 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  2         3  
  2         60  
15 2     2   6 no if "$]" >= 5.041009, feature => 'smartmatch';
  2         19  
  2         65  
16 2     2   6 no feature 'switch';
  2         3  
  2         45  
17 2     2   797 use Mojo::Base 'Mojolicious::Plugin', -signatures;
  2         17332  
  2         9  
18 2     2   2574 use Feature::Compat::Try;
  2         573  
  2         7  
19 2     2   926 use YAML::PP;
  2         117463  
  2         94  
20 2     2   1508 use Path::Tiny;
  2         24679  
  2         122  
21 2     2   896 use Mojo::JSON 'decode_json';
  2         299633  
  2         141  
22 2     2   809 use Safe::Isa;
  2         853  
  2         311  
23 2     2   1552 use OpenAPI::Modern 0.100;
  2         1362039  
  2         89  
24 2     2   16 use namespace::clean;
  2         3  
  2         9  
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 2093307 sub register ($self, $app, $config) {
  5         8  
  5         12  
  5         10  
  5         7  
32 5         14 my $stash = Mojo::Util::_stash(openapi => $app);
33              
34 5         32 try {
35 5   66     30 my $openapi = $config->{openapi_obj} // OpenAPI::Modern->new(_process_configs($config));
36              
37             # leave room for other keys in our localized stash
38 5         2339274 $stash->{openapi} = $openapi;
39             }
40             catch ($e) {
41 0         0 die 'Cannot load OpenAPI document: ', $e;
42             }
43              
44 5     15   63 $app->helper(openapi => sub ($) { $stash->{openapi} });
  15         149  
  15         4908  
  15         21  
45              
46 5         470 $app->helper(validate_request => \&_validate_request);
47 5         249 $app->helper(validate_response => \&_validate_response);
48              
49 6     6   259446 $app->hook(after_dispatch => sub ($c) {
  6         10  
  6         21  
50 6         18 $c->res->on(finish => sub ($res) { $config->{after_response}->($c) });
  6         34  
51 5 50       244 }) if $config->{after_response};
52             }
53              
54             # converts a config hash into values suitable for constructing an OpenAPI::Modern object
55 4     4   6 sub _process_configs ($config) {
  4         7  
  4         5  
56 4         7 my $schema;
57 4 100       16 if (exists $config->{schema}) {
    50          
58 2         5 $schema = $config->{schema};
59             }
60             elsif (exists $config->{document_filename}) {
61 2 100       10 if ($config->{document_filename} =~ /\.ya?ml$/) {
    50          
62 1         16 $schema = YAML::PP->new(boolean => 'JSON::PP')->load_file($config->{document_filename}),
63             }
64             elsif ($config->{document_filename} =~ /\.json$/) {
65 1         47 $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         8034 my %config_copy = %$config;
76 4         20 delete @config_copy{qw(schema document_filename document_uri after_response)};
77 4 50       11 warn 'unrecognized config option(s): ', join(', ', keys %config_copy) if keys %config_copy;
78              
79             return {
80 4   100     107 openapi_uri => $config->{document_uri} // $config->{document_filename} // '',
      100        
81             openapi_schema => $schema,
82             };
83             }
84              
85 5     5   941686 sub _validate_request ($c) {
  5         12  
  5         10  
86 5   50     13 my $options = $c->stash->{openapi} //= {};
87 5         64 return $c->openapi->validate_request($c->req, $options);
88             }
89              
90 6     6   103 sub _validate_response ($c) {
  6         9  
  6         9  
91 6   100     19 my $options = $c->stash->{openapi} //= {};
92 6         68 local $options->{request} = $c->req;
93 6         59 return $c->openapi->validate_response($c->res, $options);
94             }
95              
96             1;
97              
98             __END__