<commit_before>package Net::PMP::CLI;
use Mouse;
with 'MouseX::SimpleConfig';
with 'MouseX::Getopt';

use Net::PMP::Client;
use Data::Dump qw( dump );

has '+configfile' => ( default => $ENV{HOME} . '/.pmp.yaml' );
has 'debug'       => ( is      => 'rw', isa => 'Bool', );
has 'id'          => ( is      => 'rw', isa => 'Str', required => 1, );
has 'secret'      => ( is      => 'rw', isa => 'Str', required => 1, );
has 'host' =>
    ( is => 'rw', isa => 'Str', default => 'https://api-sandbox.pmp.io', );
has 'profile' => ( is => 'rw', isa => 'Str' );
has 'title'   => ( is => 'rw', isa => 'Str', );
has 'path'    => ( is => 'rw', isa => 'Str', );
has 'guid'    => ( is => 'rw', isa => 'Str', );
has 'parent'  => ( is => 'rw', isa => 'Str', );
has 'child'   => ( is => 'rw', isa => 'Str', );

=head1 NAME

Net::PMP::CLI - command line application for Net::PMP::Client

=head1 SYNOPSIS

 use Net::PMP::CLI;
 my $app = Net::PMP::CLI->new_with_options();
 $app->run();

=head1 DESCRIPTION

This class is used by the C<pmpc> command-line tool.
It uses L<MouseX::SimpleConfig> and L<MouseX::Getopt> to allow
for simple configuration file and option parsing.

=head1 METHODS

With the exceptions of B<run> and B<init_client> all method
names are commands.

=head2 run

Main method. Calls commands passed via @ARGV.

=cut

sub run {
    my $self = shift;

    $self->debug and dump $self;

    my @cmds = @{ $self->extra_argv };

    if ( !@cmds or $self->help_flag ) {
        $self->usage->die( { post_text => $self->commands } );
    }

    for my $cmd (@cmds) {
        if ( !$self->can($cmd) ) {
            warn "No such command $cmd\n";
            $self->usage->die( { post_text => $self->commands } );
        }
        $self->$cmd();
    }

}

sub commands {
    my $self = shift;
    my $txt  = <<EOF;
commands:
    add     --parent <guid> --child <guid>
    create  --profile <profile> --title <title>
    delete  --guid <guid>
    get     --path /path/to/resource
    groups
    users
EOF
    return $txt;
}

sub _list_items {
    my ( $self, $label, $urn ) = @_;
    my $client = $self->init_client();
    my $root   = $client->get_doc();
    my $q      = $root->query($urn);
    my $uri    = $q->as_uri( {} );
    my $res    = $client->get_doc($uri) or return;
    my $items  = $res->get_items();
    while ( my $item = $items->next ) {
        printf( "%s: %s [%s] [%s]\n",
            $label, $item->get_title, $item->get_guid, $item->get_profile, );
        if ( $item->has_items ) {
            my $iitems = $item->get_items;
            while ( my $iitem = $iitems->next ) {
                printf( " contains: %s [%s] [%s]\n",
                    $iitem->get_title, $iitem->get_guid, $item->get_profile );
            }
        }
    }
}

=head2 create

Create or update a resource via Net::PMP::Client.
Requires the C<--profile> and C<--title> options.

=cut

sub create {
    my $self    = shift;
    my $profile = $self->profile or die "--profile required for create\n";
    my $title   = $self->title or die "--title required for create\n";
    my $client  = $self->init_client;

    # verify profile first
    my $prof_doc = $self->get( '/profiles/' . $profile );
    if ( !$prof_doc ) {
        die "invalid profile: $profile\n";
    }
    my $doc = Net::PMP::CollectionDoc->new(
        version    => $client->get_doc->version,
        attributes => { title => $title, },
        links      => {
            profile => [ { href => $client->host . '/profiles/' . $profile } ]
        },
    );
    $client->save($doc);
    printf( "%s saved as %s at %s\n",
        $profile, $doc->get_title, $doc->get_uri );
}

=head2 delete

Deletes a resource. Requires the C<--guid> option.

=cut

sub delete {
    my $self   = shift;
    my $guid   = $self->guid or die "--guid required for delete\n";
    my $client = $self->init_client;
    my $doc    = $client->get_doc_by_guid($guid);
    if ( !$doc ) {
        die "Cannot delete non-existent doc $guid\n";
    }
    if ( $client->delete($doc) ) {
        printf( "Deleted %s\n", $guid );
    }
    else {<commit_msg>tweek list display to show full url, make sure we see as many users/groups as reasonable<commit_after>package Net::PMP::CLI;
use Mouse;
with 'MouseX::SimpleConfig';
with 'MouseX::Getopt';

use Net::PMP::Client;
use Data::Dump qw( dump );

has '+configfile' => ( default => $ENV{HOME} . '/.pmp.yaml' );
has 'debug'       => ( is      => 'rw', isa => 'Bool', );
has 'id'          => ( is      => 'rw', isa => 'Str', required => 1, );
has 'secret'      => ( is      => 'rw', isa => 'Str', required => 1, );
has 'host' =>
    ( is => 'rw', isa => 'Str', default => 'https://api-sandbox.pmp.io', );
has 'profile' => ( is => 'rw', isa => 'Str' );
has 'title'   => ( is => 'rw', isa => 'Str', );
has 'path'    => ( is => 'rw', isa => 'Str', );
has 'guid'    => ( is => 'rw', isa => 'Str', );
has 'parent'  => ( is => 'rw', isa => 'Str', );
has 'child'   => ( is => 'rw', isa => 'Str', );

=head1 NAME

Net::PMP::CLI - command line application for Net::PMP::Client

=head1 SYNOPSIS

 use Net::PMP::CLI;
 my $app = Net::PMP::CLI->new_with_options();
 $app->run();

=head1 DESCRIPTION

This class is used by the C<pmpc> command-line tool.
It uses L<MouseX::SimpleConfig> and L<MouseX::Getopt> to allow
for simple configuration file and option parsing.

=head1 METHODS

With the exceptions of B<run> and B<init_client> all method
names are commands.

=head2 run

Main method. Calls commands passed via @ARGV.

=cut

sub run {
    my $self = shift;

    $self->debug and dump $self;

    my @cmds = @{ $self->extra_argv };

    if ( !@cmds or $self->help_flag ) {
        $self->usage->die( { post_text => $self->commands } );
    }

    for my $cmd (@cmds) {
        if ( !$self->can($cmd) ) {
            warn "No such command $cmd\n";
            $self->usage->die( { post_text => $self->commands } );
        }
        $self->$cmd();
    }

}

sub commands {
    my $self = shift;
    my $txt  = <<EOF;
commands:
    add     --parent <guid> --child <guid>
    create  --profile <profile> --title <title>
    delete  --guid <guid>
    get     --path /path/to/resource
    groups
    users
EOF
    return $txt;
}

sub _list_items {
    my ( $self, $label, $urn ) = @_;
    my $client = $self->init_client();
    my $root   = $client->get_doc();
    my $q      = $root->query($urn);
    my $uri    = $q->as_uri( { limit => 200 } );     # TODO random big number
    my $res    = $client->get_doc($uri) or return;
    my $items  = $res->get_items();
    while ( my $item = $items->next ) {
        my $profile = $item->get_profile;
        $profile =~ s,^.+/,,;
        printf( "%s [%s]: %s [%s]\n",
            $label, $profile, $item->get_title, $item->get_uri, );
        if ( $item->has_items ) {
            my $iitems = $item->get_items;
            while ( my $iitem = $iitems->next ) {
                my $iprofile = $item->get_profile;
                $iprofile =~ s,^.+/,,;
                printf( " contains: %s [%s] [%s]\n",
                    $iitem->get_title, $iitem->get_uri, $iprofile );
            }
        }
    }
}

=head2 create

Create or update a resource via Net::PMP::Client.
Requires the C<--profile> and C<--title> options.

=cut

sub create {
    my $self    = shift;
    my $profile = $self->profile or die "--profile required for create\n";
    my $title   = $self->title or die "--title required for create\n";
    my $client  = $self->init_client;

    # verify profile first
    my $prof_doc = $self->get( '/profiles/' . $profile );
    if ( !$prof_doc ) {
        die "invalid profile: $profile\n";
    }
    my $doc = Net::PMP::CollectionDoc->new(
        version    => $client->get_doc->version,
        attributes => { title => $title, },
        links      => {
            profile => [ { href => $client->host . '/profiles/' . $profile } ]
        },
    );
    $client->save($doc);
    printf( "%s saved as '%s' at %s\n",
        $profile, $doc->get_title, $doc->get_uri );
}

=head2 delete

Deletes a resource. Requires the C<--guid> option.

=cut

sub delete {
    my $self   = shift;
    my $guid   = $self->guid or die "--guid required for delete\n";
    my $client = $self->init_client;
    my $doc    = $client->get_doc_by_guid($guid);
    if ( !$doc ) {
        die "Cannot delete non-existent doc $guid\n";
    }
    if ( $client->delete($doc) ) {
        printf( "Deleted %s\n", $guid );
    }
    else {
{'fraction_non_alphanumeric': 0.19216617210682493, 'fraction_numerical': 0.002492581602373887, 'mean_word_length': 2.30690737833595, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 204, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1258925', 'n_tokens_mistral': 3027, 'n_tokens_neox': 2974, 'n_words': 825}