this adds a tool to generate the apidoc without the need to execute nginx/catalyst: - swagger/openapi .json/.yaml - html apidoc Change-Id: I259e7c32faa5d419161402324862c07fa1d2d7edmr11.0
							parent
							
								
									d93f4d7bed
								
							
						
					
					
						commit
						77602745a7
					
				| @ -0,0 +1,445 @@ | |||||||
|  | #!/usr/bin/perl | ||||||
|  | 
 | ||||||
|  | use strict; | ||||||
|  | use warnings; | ||||||
|  | 
 | ||||||
|  | use Getopt::Long; | ||||||
|  | use Pod::Usage qw(pod2usage); | ||||||
|  | use Config::General qw(); | ||||||
|  | use File::Slurp qw(); | ||||||
|  | use XML::Simple qw(); | ||||||
|  | 
 | ||||||
|  | #use lib "/home/rkrenn/sipwise/git/ngcp-schema/lib"; | ||||||
|  | #use lib "/home/rkrenn/sipwise/git/sipwise-base/lib"; | ||||||
|  | #use lib "/usr/local/devel/ngcp-schema/lib"; | ||||||
|  | #use lib "/usr/local/devel/sipwise-base/lib"; | ||||||
|  | #use lib "/home/rkrenn/sipwise/git/ngcp-panel/lib"; | ||||||
|  | #use lib "/usr/local/devel/ngcp-panel/lib"; | ||||||
|  | 
 | ||||||
|  | use NGCP::Schema::Config qw(); | ||||||
|  | use NGCP::Schema qw(); | ||||||
|  | use NGCP::Panel::Controller::API::Root qw(); | ||||||
|  | 
 | ||||||
|  | #use NGCP::Panel::Utils::ConfigFilenames qw(); | ||||||
|  | 
 | ||||||
|  | my @panel_configs = qw( | ||||||
|  |    /etc/ngcp-panel/ngcp_panel.conf | ||||||
|  |    /etc/ngcp_panel.conf | ||||||
|  |    /ngcp_panel.conf | ||||||
|  | ); | ||||||
|  | #/home/rkrenn/sipwise/git/vagrant-ngcp/ngcp_panel.conf | ||||||
|  | #/usr/local/devel/vagrant-ngcp/ngcp_panel.conf | ||||||
|  | 
 | ||||||
|  | my @provisioning_configs = qw( | ||||||
|  |    /etc/ngcp-panel/provisioning.conf | ||||||
|  | ); | ||||||
|  | #/etc/ngcp-panel/provisioning.conf | ||||||
|  | #/home/rkrenn/sipwise/git/vagrant-ngcp/provisioning.conf | ||||||
|  | #/usr/local/devel/vagrant-ngcp/provisioning.conf | ||||||
|  | 
 | ||||||
|  | my %db = ( | ||||||
|  |     host => '192.168.0.96', | ||||||
|  |     port => 3306, | ||||||
|  |     user => 'root', | ||||||
|  |     password => undef, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | my $help; | ||||||
|  | my $log_level; | ||||||
|  | my $doc_file; | ||||||
|  | my $doc_format = 'json'; | ||||||
|  | 
 | ||||||
|  | GetOptions( | ||||||
|  |     ( map { ('db-' . $_ . ':s') => \$db{$_}; } keys %db ), | ||||||
|  |     "format:s" => \$doc_format, | ||||||
|  |     "file:s" => \$doc_file, | ||||||
|  |     'log-level:s' => \$log_level, | ||||||
|  |     "help|?" => \$help, | ||||||
|  | ) or pod2usage(2); | ||||||
|  | pod2usage(1) if $help; | ||||||
|  | my %mock_objs = (); | ||||||
|  | my $c = _create_c(); | ||||||
|  | 
 | ||||||
|  | my $controller = NGCP::Panel::Controller::API::Root->new; | ||||||
|  | $controller->GET($c); | ||||||
|  | 
 | ||||||
|  | _write_file($c->response->body); | ||||||
|  | 
 | ||||||
|  | exit(0); | ||||||
|  | 
 | ||||||
|  | sub _write_file { | ||||||
|  | 
 | ||||||
|  |     my ($str) = @_; | ||||||
|  |     if (defined $doc_file) { | ||||||
|  |         open(my $fh, '>', $doc_file) or die('cannot open file ' . $doc_file . ': ' . $!); | ||||||
|  |         binmode($fh); | ||||||
|  |         print $fh $str; | ||||||
|  |         close $fh; | ||||||
|  |     } else { | ||||||
|  |         print $str; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub _create_c { | ||||||
|  | 
 | ||||||
|  |     #{ | ||||||
|  |     #    no strict "refs";  ## no critic (ProhibitNoStrict) | ||||||
|  |     #    no warnings 'redefine'; | ||||||
|  |     #    *{'NGCP::Panel::Utils::ConfigFilenames::get_panel_config_filename'} = sub { | ||||||
|  |     #        my $panel_config_file; | ||||||
|  |     #        for my $path(@panel_configs) { | ||||||
|  |     #            if (-f $path) { | ||||||
|  |     #                $panel_config_file = $path; | ||||||
|  |     #                last; | ||||||
|  |     #            } | ||||||
|  |     #        } | ||||||
|  |     #        $panel_config_file //= 'etc/ngcp_panel.conf'; | ||||||
|  |     #        return $panel_config_file; | ||||||
|  |     #    }; | ||||||
|  |     #    *{'NGCP::Panel::Utils::ConfigFilenames::get_provisioning_config_filename'} = sub { | ||||||
|  |     #        foreach my $provisioning_config (@provisioning_configs) { | ||||||
|  |     #            return $provisioning_config if -f $provisioning_config; | ||||||
|  |     #        } | ||||||
|  |     #    }; | ||||||
|  |     #    *{'NGCP::Schema::Config::get_config_filename'} = sub { | ||||||
|  |     #        foreach my $provisioning_config (@provisioning_configs) { | ||||||
|  |     #            return $provisioning_config if -f $provisioning_config; | ||||||
|  |     #        } | ||||||
|  |     #    }; | ||||||
|  |     #} | ||||||
|  |      | ||||||
|  |     { | ||||||
|  |         print "loading panel components ...\n"; | ||||||
|  |         my $module = 'NGCP::Panel'; | ||||||
|  |         my $file = $module; | ||||||
|  |         $file =~ s[::][/]g; | ||||||
|  |         $file .= '.pm'; | ||||||
|  |         require $file; | ||||||
|  |         $module->import; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     my $config = _get_panel_config(); | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         no strict "refs";  ## no critic (ProhibitNoStrict) | ||||||
|  |         *{'NGCP::Schema::set_transaction_isolation'} = sub { | ||||||
|  |             my ($self,$level) = @_; | ||||||
|  |             return $self->storage->dbh_do( | ||||||
|  |                 sub { | ||||||
|  |                   my ($storage, $dbh, @args) = @_; | ||||||
|  |                   $dbh->do("SET TRANSACTION ISOLATION LEVEL " . $args[0]); | ||||||
|  |                 }, | ||||||
|  |                 $level, | ||||||
|  |             ); | ||||||
|  |         }; | ||||||
|  |         *{'NGCP::Schema::set_wait_timeout'} = sub { | ||||||
|  |             my ($self,$timeout) = @_; | ||||||
|  |             return $self->storage->dbh_do( | ||||||
|  |                 sub { | ||||||
|  |                   my ($storage, $dbh, @args) = @_; | ||||||
|  |                   $dbh->do("SET SESSION wait_timeout = " . $args[0]); | ||||||
|  |                 }, | ||||||
|  |                 $timeout, | ||||||
|  |             ); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |     my $db = _get_schema(); | ||||||
|  | 
 | ||||||
|  |     my $req = sub { | ||||||
|  |         my $self = shift; | ||||||
|  |         return _create_mock('_request', | ||||||
|  |             _param => { | ||||||
|  |                 ($doc_format eq 'swagger-yml' ? ('swagger' => 'yml') : ()), | ||||||
|  |                 ($doc_format eq 'swagger-json' ? ('swagger' => 'json') : ()), | ||||||
|  |                 #'oldapidoc' => 1, | ||||||
|  |             }, | ||||||
|  |             _header => { | ||||||
|  |                 ($doc_format eq 'json' ? ('Accept' => 'application/json') : ()) | ||||||
|  |             }, | ||||||
|  |             param => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 my $key = shift; | ||||||
|  |                 return $self->{param}->{$key} if $key; | ||||||
|  |                 return $self->{param}; | ||||||
|  |             }, | ||||||
|  |             path => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 return 'api/'; | ||||||
|  |             }, | ||||||
|  |             params => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 return $self->{param}; | ||||||
|  |             }, | ||||||
|  |             query_params => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 return $self->{param}; | ||||||
|  |             }, | ||||||
|  |             header => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 my $key = shift; | ||||||
|  |                 return $self->{header}->{$key} if $key; | ||||||
|  |                 return $self->{header}; | ||||||
|  |             }, | ||||||
|  |         ); | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     my $res = sub { | ||||||
|  |         my $self = shift; | ||||||
|  |         return _create_mock('_response', | ||||||
|  |             _body => undef, | ||||||
|  |             _code => undef, | ||||||
|  |             _header => { | ||||||
|  |             }, | ||||||
|  |             body => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 if (scalar @_) { | ||||||
|  |                     $self->{body} = shift; | ||||||
|  |                 } | ||||||
|  |                 return $self->{body}; | ||||||
|  |             }, | ||||||
|  |             code => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 if (scalar @_) { | ||||||
|  |                     $self->{code} = shift; | ||||||
|  |                 } | ||||||
|  |                 return $self->{code}; | ||||||
|  |             }, | ||||||
|  |             headers => sub { | ||||||
|  |                 my $self = shift; | ||||||
|  |                 my $key = shift; | ||||||
|  |                 return $self->{header}->{$key} if $key; | ||||||
|  |                 return $self->{header}; | ||||||
|  |             }, | ||||||
|  |         ); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     $c = _create_mock('c', | ||||||
|  |         _stash => { | ||||||
|  | 
 | ||||||
|  |         }, | ||||||
|  |         stash => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             my $key = shift; | ||||||
|  |             return $self->{stash}->{$key} if $key; | ||||||
|  |             return $self->{stash}; | ||||||
|  |         }, | ||||||
|  |         _model => { | ||||||
|  |             DB => $db, | ||||||
|  |         }, | ||||||
|  |         model => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             my $key = shift; | ||||||
|  |             return $self->{model}->{$key} if $key; | ||||||
|  |             return $self->{model}; | ||||||
|  |         }, | ||||||
|  |         log => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             return _create_mock('log', | ||||||
|  |                 debug => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     my $str = shift; | ||||||
|  |                     my @params = @_; | ||||||
|  |                     print $str . "\n" if ($log_level and grep { $_ eq lc($log_level); } qw(debug)); | ||||||
|  |                 }, | ||||||
|  |                 info => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     my $str = shift; | ||||||
|  |                     my @params = @_; | ||||||
|  |                     print $str . "\n" if ($log_level and grep { $_ eq lc($log_level); } qw(info debug)); | ||||||
|  |                 }, | ||||||
|  |                 warn => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     my $str = shift; | ||||||
|  |                     my @params = @_; | ||||||
|  |                     print $str . "\n" if ($log_level and grep { $_ eq lc($log_level); } qw(warn info debug)); | ||||||
|  |                 }, | ||||||
|  |                 error => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     my $str = shift; | ||||||
|  |                     my @params = @_; | ||||||
|  |                     print $str . "\n" if ($log_level and grep { $_ eq lc($log_level); } qw(error warn info debug)); | ||||||
|  |                 }, | ||||||
|  |             ); | ||||||
|  |         }, | ||||||
|  |         config => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             return $config; | ||||||
|  |         }, | ||||||
|  |         user => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             return _create_mock('user', | ||||||
|  |                 roles => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     return 'admin'; | ||||||
|  |                 }, | ||||||
|  |                 read_only => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     return 0; | ||||||
|  |                 }, | ||||||
|  |                 is_system => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     return 0; | ||||||
|  |                 }, | ||||||
|  |                 is_superuser => sub { | ||||||
|  |                     my $self = shift; | ||||||
|  |                     return 1; | ||||||
|  |                 }, | ||||||
|  |             ); | ||||||
|  |         }, | ||||||
|  |         loc => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             my $str = shift; | ||||||
|  |             my @params = @_; | ||||||
|  |             for (my $i = 1; $i <= scalar @params; $i++) { | ||||||
|  |                 my $subst = quotemeta("[_$i]"); | ||||||
|  |                 my $repl = $params[$i - 1]; | ||||||
|  |                 $str =~ s/$subst/$repl/g; | ||||||
|  |             } | ||||||
|  |             return $str; | ||||||
|  |         }, | ||||||
|  |         detach => sub { | ||||||
|  |             my $self = shift; | ||||||
|  |             my $sub = shift; | ||||||
|  |             my ($package, $filename, $line) = caller; | ||||||
|  |             return $package->$sub($self); | ||||||
|  |         }, | ||||||
|  |         view => sub { | ||||||
|  |             return 'NGCP::Panel'->view('View::TT');   | ||||||
|  |         }, | ||||||
|  |         request => $req, | ||||||
|  |         req => $req, | ||||||
|  |         response => $res, | ||||||
|  |         res => $res, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub _create_mock { | ||||||
|  |     my $class = shift; | ||||||
|  |     return $mock_objs{$class} if exists $mock_objs{$class}; | ||||||
|  |     my %members = @_; | ||||||
|  |     my $obj = bless({},$class); | ||||||
|  |     foreach my $member (keys %members) { | ||||||
|  |         if ('CODE' eq ref $members{$member}) { | ||||||
|  |             no strict "refs";  ## no critic (ProhibitNoStrict) | ||||||
|  |             *{$class.'::'.$member} = $members{$member}; | ||||||
|  |         } else { | ||||||
|  |             $obj->{substr($member,1)} = $members{$member}; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     $mock_objs{$class} = $obj; | ||||||
|  |     return $obj; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub _get_schema { | ||||||
|  | 
 | ||||||
|  |     my $schema; | ||||||
|  |     if ($db{host}) { | ||||||
|  |         $schema = NGCP::Schema->connect({ | ||||||
|  |             dsn                 => "DBI:mysql:database=provisioning;host=$db{host};port=$db{port}", | ||||||
|  |             user                => $db{user}, | ||||||
|  |             ( exists $db{password} ? (password => $db{password}) : ()), | ||||||
|  |             mysql_enable_utf8   => "1", | ||||||
|  |             on_connect_do       => "SET NAMES utf8mb4", | ||||||
|  |             quote_char          => "`", | ||||||
|  |         }); | ||||||
|  |         $schema->set_wait_timeout(3600); | ||||||
|  |         return $schema; | ||||||
|  |     } else { | ||||||
|  |         foreach my $provisioning_config (@provisioning_configs) { | ||||||
|  |             if (-f $provisioning_config) { | ||||||
|  |                 my $conf = XML::Simple->new->XMLin($provisioning_config, ForceArray => 1); | ||||||
|  |                 if ($conf && $conf->{ngcp_connect_info}) { | ||||||
|  |                     my $connectors; | ||||||
|  |                     if (exists $conf->{ngcp_connect_info}->[0]->{connectors}) { | ||||||
|  |                          $connectors = $conf->{ngcp_connect_info}->[0]->{connectors}; | ||||||
|  |                     } else { | ||||||
|  |                         $connectors = $conf->{ngcp_connect_info}; | ||||||
|  |                     } | ||||||
|  |                     $connectors //= []; | ||||||
|  |                     $schema = NGCP::Schema->connect($connectors->[0]); | ||||||
|  |                     $schema->set_wait_timeout(3600); | ||||||
|  |                     return $schema; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         die("no provisioning.conf found\n") unless $schema; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub _get_panel_config { | ||||||
|  | 
 | ||||||
|  |     my $config; | ||||||
|  |     foreach my $panel_config (@panel_configs) { | ||||||
|  |         if( -f $panel_config ){ | ||||||
|  |             my $catalyst_config = Config::General->new($panel_config); | ||||||
|  |             my %config = $catalyst_config->getall(); | ||||||
|  |             $config = \%config; | ||||||
|  |             last; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     die("no ngcp_panel.conf found\n") unless $config; | ||||||
|  |     return $config; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | __END__ | ||||||
|  | 
 | ||||||
|  | =head1 NAME | ||||||
|  | 
 | ||||||
|  | ngcp-api-generate-doc - generate apidoc output | ||||||
|  | 
 | ||||||
|  | =head1 SYNOPSIS | ||||||
|  | 
 | ||||||
|  | E<9>B<ngcp-api-generate-doc> [I<options>] | ||||||
|  | 
 | ||||||
|  | =head1 DESCRIPTION | ||||||
|  | 
 | ||||||
|  | This program allows to generate the NGCP Rest-API documentation offline (without running nginx). | ||||||
|  | 
 | ||||||
|  | =head1 OPTIONS | ||||||
|  | 
 | ||||||
|  | =over 4 | ||||||
|  | 
 | ||||||
|  | =item B<--help> | ||||||
|  | 
 | ||||||
|  | Print a brief help message and exits. | ||||||
|  | 
 | ||||||
|  | =item B<--format=>I<format> | ||||||
|  | 
 | ||||||
|  | Specify the apidoc format (json/swagger-json/swagger-yml). | ||||||
|  | 
 | ||||||
|  | =item B<--file=>I<filename> | ||||||
|  | 
 | ||||||
|  | Specify a file to write the apidoc output to. | ||||||
|  | 
 | ||||||
|  | =item B<--log-level> | ||||||
|  | 
 | ||||||
|  | Verbosity of printed messages while processing (debug, info, warn, error). | ||||||
|  | 
 | ||||||
|  | =back | ||||||
|  | 
 | ||||||
|  | =head1 AUTHOR | ||||||
|  | 
 | ||||||
|  | Sipwise Development Team C<< <support@sipwise.com> >> | ||||||
|  | 
 | ||||||
|  | =head1 LICENSE | ||||||
|  | 
 | ||||||
|  | This software is Copyright © 2020 by Sipwise GmbH, Austria. | ||||||
|  | 
 | ||||||
|  | This program is free software; you can redistribute it and/or modify | ||||||
|  | it under the terms of the GNU General Public License as published by | ||||||
|  | the Free Software Foundation; either version 3 of the License, or | ||||||
|  | (at your option) any later version. | ||||||
|  | 
 | ||||||
|  | This program is distributed in the hope that it will be useful, | ||||||
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | You should have received a copy of the GNU General Public License | ||||||
|  | along with this package.  If not, see <https://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | =cut | ||||||
					Loading…
					
					
				
		Reference in new issue