#!/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 NGCP::Schema::Config qw(); use NGCP::Schema qw(); use NGCP::Panel::Controller::API::Root qw(); my @panel_configs = qw( /etc/ngcp-panel/ngcp_panel.conf /etc/ngcp_panel.conf /ngcp_panel.conf ); my @provisioning_configs = qw( /etc/ngcp-panel/provisioning.conf ); my %db = ( host => undef, 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 { { 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 [I] =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 Specify the apidoc format (json/swagger-json/swagger-yml). =item B<--file=>I 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<< >> =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 . =cut