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