Respite

 view release on metacpan or  search on metacpan

lib/Respite/CommandLine.pm  view on Meta::CPAN

    my ($self, $preload) = @_;
    return $self->{'dispatch_factory'} ||= do {
        my $meta = $self->api_meta || $self->dispatch_class || throw "Missing one of api_meta or dispatch_class";
        if (!ref $meta) {
            (my $file = "$meta.pm") =~ s|::|/|g;
            throw "Failed to load dispatch class", {class => $meta, file => $file, msg => $@} if !$meta->can('new') && !eval { require $file };
            throw "Specified class does not have a run_method method", {class => $meta} if ! $meta->can('run_method');
            sub { $meta->new(@_) };
        } elsif ($meta->{'remote'}) {
            require Respite::Client;
            sub { Respite::Client->new({%{shift() || {}}, %$meta}) };
        } else {
            require Respite::Base;
            Respite::Base->new({api_meta => $meta})->api_preload if $preload;
            sub { Respite::Base->new({%{shift() || {}}, api_meta => $meta}) };
        }
    };
}

###----------------------------------------------------------------###

sub run_commandline { shift->run(@_) }

sub run {
    my ($self, $args) = @_;
    $self = $self->new($args) if ! ref($self);

    my $obj = $self->dispatch_factory->();
    my $ARGV = $args->{'argv'} || $self->{'argv'} || \@ARGV;

    my $method = shift(@$ARGV) || return print $self->_pod($obj, {brief => 1});
    return print $self->_pod($obj, {format => $1}) if $method =~ /^-{0,2}(help|h|pod|p)$/;
    return print $self->_pod($obj, {method => $method, format => $_}) for grep {/^-{1,2}(help|h|pod|p)$/} @ARGV;

    throw "Odd number of args passed to commandline. If you want the last value to be undef pass a :null", {argv => $ARGV, _pretty=>1} if @$ARGV % 2;
    my $req = {@$ARGV};
    throw "Cannot use '' as a keyname - possible invalid args", {argv => $ARGV} if exists $req->{''};
    foreach my $key (keys %$req) { $req->{$key} = __PACKAGE__->can("_$1")->() if $req->{$key} && $req->{$key} =~ /^:(null|true|false)$/ }
    $req = Data::URIEncode::flat_to_complex($req) || {} if !$self->{'no_data_uriencode'} && eval { require Data::URIEncode };

    my $data = $self->_run_method($obj,$method, $req);
    my $meta = $ENV{'SHOW_META'} ? $self->_run_method($obj,"${method}__meta", $req) : undef;
    $self->print_data($data, $req, $meta);
    exit(1) if ref($data) && $data->{'error'};
}

sub run_method {
    my ($self, $method, $args) = @_;
    $self = $self->new($args) if ! ref($self);
    my $obj = $self->dispatch_factory->();
    return $self->_run_method($obj, $method, $args);
}

sub _run_method {
    my ($self, $obj, $method, $args, $extra) = @_;

    local $args->{'_c'} = ['commandline'] if $obj->can('config') ? !$obj->config(no_trace => undef) : 1;

    local $obj->{'remote_ip'}   = local $obj->{'api_ip'} = ($ENV{'REALUSER'} || $ENV{'SUDO_USER'}) ? 'sudo' : 'cmdline';
    local $obj->{'api_brand'}   = $ENV{'BRAND'} || $ENV{'PROV'} if $obj->isa('Respite::Base') && ($ENV{'BRAND'} || $ENV{'PROV'});
    local $obj->{'remote_user'} = $ENV{'REALUSER'} || $ENV{'SUDO_USER'} || $ENV{'REMOTE_USER'} || $ENV{'USER'} || (getpwuid($<))[0] || '-unknown-';
    local $obj->{'token'}       = $self->{'token'} || $ENV{'ADMIN_Respite_TOKEN'} if $self->{'token'} || $ENV{'ADMIN_Respite_TOKEN'};
    local $obj->{'transport'}   = 'cmdline';
    $obj->commandline_init($method, $args, $self) if $obj->can('commandline_init');

    my $run = sub {
        my $data = eval { $obj->can('run_method') ? $obj->run_method(@_) : $obj->$method($args, ($extra ? $extra : ())) };
        $data = $@ if ! ref $data;
        return !ref($data) ? {error => 'Commandline failed', msg => $data} : (blessed($data) && $data->can('data')) ? $data->data : $data;
    };
    my $ref = $run->($method, $args, $extra);
    while ($ref->{'error'}) {
        last if !$ref->{'type'} || $ref->{'type'} !~ /^token_\w+$/;
        last if $self->{'no_token_retry'};
        warn "Prompting for authorization and retry ($ref->{'type'}: $ref->{'error'})\n";
        eval { require IO::Prompt } || throw "Please install IO::Prompt to authenticate from commandline", {msg => $@};
        my $user = ''.IO::Prompt::prompt("  Web Auth Username: ", -d => $obj->{'remote_user'}) || $obj->{'remote_user'};
        my $pass = ''.IO::Prompt::prompt("  Web Auth Password ($user): ", -e => '*') || throw "Cannot proceed without password";
        my $key  = !$obj->can('config') ? $config::config{'plaintext_public_key'}
            : $obj->config(plaintext_public_key => sub { $obj->config(plaintext_public_key => sub { $obj->_configs->{'plaintext_public_key'} }, 'emp_auth') });
        if (!$key) {
            warn "  Could not find plaintext_public_key in config - sending plaintext password\n";
        } elsif (!eval { require Crypt::OpenSSL::RSA }) {
            warn "  (Crypt::OpenSSL::RSA is not installed - install to avoid sending plaintext password)\n";
        } else {
            my $c = Crypt::OpenSSL::RSA->new_public_key($key);
            my $len = length($pass) + 1;
            $pass = pack 'u*', $c->encrypt(pack "Z$len", $pass);
            $pass = "RSA".length($pass).":$pass";
        }
        $obj->{'token'} = "$user/i:cmdline/$pass";
        $ref = $run->(hello => {test_auth => 1});
        $self->{'token'} = $obj->{'token'} = $ref->{'token'} || throw "Did not get a token back from successful test_auth", {data => $ref};
        warn "\nexport ADMIN_Respite_TOKEN=$obj->{'token'}\n\n";
        $ref = $run->($method, $args, $extra);
    }
    return $ref;
}

sub print_data {
    my ($self, $data, $args, $meta) = @_;
    if ($ENV{'CSV'} and my @fields = grep {ref($data->{$_}) eq 'ARRAY' && ref($data->{$_}->[0]) eq 'HASH'} sort keys %$data) {
        require Text::CSV_XS;
        my $csv = Text::CSV_XS->new({eol => "\n"});
        foreach my $field (@fields) {
            print "----- $field -------------------------\n" if @fields > 1;
            my @keys = sort {($a eq 'id') ? -1 : ($b eq 'id') ? 1 : $a cmp $b } keys %{ $data->{'rows'}->[0] };
            $csv->print(\*STDOUT, \@keys);
            $csv->print(\*STDOUT, [map {ref($_) eq 'ARRAY' ? join(",",@$_) : ref($_) eq 'HASH' ? join(",",%$_) : $_} @$_{@keys}]) for @{ $data->{'rows'} };
        }
        exit;
    }
    if ($ENV{'YAML'}) {
        eval { require YAML } || throw "Could not load YAML for output", {msg => $@};
        print YAML->new->Dump($data);
    } elsif ($ENV{'JSON'} || ! eval { require Text::PrettyTable }) {
        eval { require JSON } || throw "Could not load JSON for output", {msg => $@};
        my $json = JSON->new->utf8->allow_nonref->convert_blessed->pretty->canonical;
        print "meta = ".$json->encode($meta) if $ENV{'SHOW_META'};
        print "args = ".$json->encode($args);
        print "data = ".$json->encode($data);



( run in 2.145 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )