The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Connector::Iterator;

use strict;
use warnings;
use English;
use Moose;
use DateTime;
use Data::Dumper;

extends 'Connector';

has 'BASECONNECTOR' => ( is => 'ro', required => 1 );

# Location must not be used
has '+LOCATION' => ( required => 0, 'isa' => 'Undef' );

has target => (
    is  => 'rw',
    isa => 'Undef|ArrayRef',
    lazy => 1,
    builder => '_init_target',
);

has skip_on_error => (
    is  => 'rw',
    isa => 'Bool',
    default => 0,
);

sub _init_target {

    my $self = shift;

    # the connectors prefix points to the root node of the target list    
    my @target_node = $self->_build_path_with_prefix();
    
    if (!$self->BASECONNECTOR()->exists( \@target_node )) {
        $self->log()->warn( 'Target node does not exists ' . join(".", \@target_node) );
        return;
    }

    $self->log()->debug( 'Node with targets' . Dumper \@target_node );
    
    my @targets = $self->BASECONNECTOR()->get_keys( \@target_node );
    
    if (!scalar @targets) {
        $self->log()->warn( 'No targets found!' );
        return;
    }
     
    $self->log()->debug( 'Targets ' . Dumper \@targets );
    
    return \@targets;
}

sub set {
    
    my $self = shift;
    my $item = shift;
    my $data = shift;

    my $targets = $self->target();
    
    if (!$targets) {
        $self->log()->error( 'No targets found!' );
        return $self->_node_not_exists();
    }
    
    my @item_path = $self->_build_path( $item );
    $self->log()->debug( 'Item path' . Dumper \@item_path);
    
    # Initialize the base connector
    my $baseconn = $self->BASECONNECTOR();

    my $result;
    
    foreach my $target (@{$targets}) {
        
        $self->log()->debug( 'Publication to ' . $target . ' with item ' . Dumper $item );
        
        my @publication_target = $self->_build_path_with_prefix( [ $target, @item_path ] );
        
        $result->{$target} = '';
        my $res;
        if ($self->skip_on_error()) {
            eval{ $res = $baseconn->set( \@publication_target , $data ); };
            if ($EVAL_ERROR) {
                $EVAL_ERROR =~ /\A(.{1,200})/;
                $result->{$target} = $1;
            }
        } else {
            $res = $baseconn->set( \@publication_target , $data );
        }
        $self->log()->debug('Publication result: ' . Dumper $res );
    }

    return $result;    
}

1;

__END__;


=head1 Name

Connector::Iterator

=head1 Description

Helper to perform a I<set> operation over a list of connector endpoints
while handing errors individually for each connector. The return value
is a hashref with the processed target names as key and an empty value
if no errors occured and the exception message if the target failed. You
must set I<skip_on_error> to enable handling of expcetions, otherwise 
they just bubble up and terminate execution of the loop. 

Intended use case: write the same data to multiple targets by using 
multiple connectors. Failed write attemps can be skipped or queued
and redone

=head2 Supported methods

set

=head1 Configuration Example

    my $target = OpenXPKI::Connector::Iterator->new({
        BASECONNECTOR => $config,
        PREFIX => $prefix
    });

    $target->set( [ $data->{issuer}{CN}[0] ], $data );
    
=head1 OPTIONS

=over 

=item BASECONNECTOR

Reference to the connector for the underlying config.

=item PREFIX

The full path to the node above the targets.

=item target

List of targets to iterate thru, must be single path elements!

=item skip_on_error

By default, exceptions from the called connectors bubble up, the loop
over the targets terminate. If set, all connectors are processed and 
any exceptions are returned in the result.

=back