App-SD

 view release on metacpan or  search on metacpan

lib/App/SD/Replica/rt/PullEncoder.pm  view on Meta::CPAN

    my $transactions = shift;

    # undefine empty fields, we'll delete after cleaning
    $ticket->{$_} = undef for
        grep defined $ticket->{$_} && $ticket->{$_} eq '',
        keys %$ticket;

    $ticket->{'id'} =~ s/^ticket\///g;

    $ticket->{ $self->sync_source->uuid . '-' . lc($_) } = delete $ticket->{$_}
        for qw(Queue id);

    delete $ticket->{'Owner'} if lc($ticket->{'Owner'}) eq 'nobody';
    $ticket->{'Owner'} = $self->resolve_user_id_to( email_address => $ticket->{'Owner'} )
        if $ticket->{'Owner'};

    # normalize names of watchers to variant with suffix 's'
    foreach my $field (qw(Requestor Cc AdminCc)) {
        if ( defined $ticket->{$field} && defined $ticket->{$field .'s'} ) {
            die "It's impossible! Ticket has '$field' and '${field}s'";
        } elsif ( defined $ticket->{$field} ) {
            $ticket->{$field .'s'} = delete $ticket->{$field};
        }
    }

    for my $date (grep defined $ticket->{$_}, qw(Created Resolved Told LastUpdated Due Starts Started)) {
        my $dt = App::SD::Util::string_to_datetime($ticket->{$date});
        if ($dt) {
            $ticket->{$date} =  $dt->ymd('-')." ".$dt->hms(":");
        } else {
            delete $ticket->{$date}
        }
    }

    $ticket->{$_} =~ s/ minutes$//
        for grep defined $ticket->{$_}, qw(TimeWorked TimeLeft TimeEstimated);

    $ticket->{'Status'} =~ $self->translate_status($ticket->{'Status'});

    # delete undefined and empty fields
    delete $ticket->{$_} for
        grep !defined $ticket->{$_} || $ticket->{$_} eq '',
        keys %$ticket;

    return $ticket, {%$ticket};
}

=head2 find_matching_tickets query => QUERY

Returns an RT::Client ticket collection for all tickets found matching your QUERY string.

=cut

sub find_matching_tickets {
    my $self = shift;
    my %args = validate(@_,{query => 1});
    my $query = $args{query};
    # If we've ever synced, we can limit our search to only newer things
    if ( my $before = $self->_only_pull_tickets_modified_after ) {
       $query = "($query) AND LastUpdated >= '" . $before->ymd('-') . " " . $before->hms(':') . "'";
        $self->sync_source->log( "Skipping all tickets not updated since " . $before->iso8601 );
    }
    return [map {
        Prophet::CLI->end_pager();
        # squelch chatty RT::Client::REST "Unknown key" warnings unless debugging turned on
        local $SIG{__WARN__} = sub { $self->sync_source->log_debug(@_) };
        my $hash = $self->sync_source->rt->show( type => 'ticket', id => $_ );
        $hash->{id} =~ s|^ticket/||g;
        $hash
    } $self->sync_source->rt->search( type => 'ticket', query => $query )];
}


=head2 find_matching_transactions { ticket => $id, starting_transaction => $num }

Returns a reference to an array of all transactions (as hashes) on ticket $id after transaction $num.

=cut

sub find_matching_transactions {
    my $self = shift;
    my %args = validate( @_, { ticket => 1, starting_transaction => 1 } );
    my @txns;

    my $rt_handle = $self->sync_source->rt;

    my $ticket_id = $self->ticket_id( $args{ticket} );

    my $latest = $self->sync_source->app_handle->handle->last_changeset_from_source(
        $self->sync_source->uuid_for_remote_id($ticket_id) ) || 0;

    for my $txn ( sort $rt_handle->get_transaction_ids( parent_id => $ticket_id ) ) {

        # Skip things calling code told us to skip
        next if $txn < $args{'starting_transaction'};

        # skip things we had on our last pull
        next if $txn <= $latest;

        # Skip things we've pushed
        next if $self->sync_source->foreign_transaction_originated_locally( $txn, $ticket_id );
        my $txn_hash = $rt_handle->get_transaction( parent_id => $ticket_id, id => $txn, type => 'ticket' );
        if ( my $attachments = delete $txn_hash->{'Attachments'} ) {
            for my $attach ( split( /\n/, $attachments ) ) {
                next unless ( $attach =~ /^(\d+):/ );
                my $id = $1;
                my $a = $rt_handle->get_attachment( parent_id => $ticket_id, id => $id );

                push( @{ $txn_hash->{_attachments} }, $a ) if ( $a->{Filename} );

            }

        }
        push @txns,
            {
            timestamp => App::SD::Util::string_to_datetime( $txn_hash->{Created} ),
            serial    => $txn_hash->{id},
            object    => $txn_hash
            };
    }
    return \@txns;

lib/App/SD/Replica/rt/PullEncoder.pm  view on Meta::CPAN


}


sub resolve_user_id_to {
    my $self = shift;
    my $attr = shift;
    my $id   = shift;
    return undef unless $id;

    local $@;
    my $user = eval {
       Prophet::CLI->end_pager();
       # squelch chatty RT::Client::REST "Unknown key" warnings
       local $SIG{__WARN__} = sub { $self->sync_source->log_debug(@_) };
       RT::Client::REST::User->new( rt => $self->sync_source->rt, id => $id )->retrieve;
    };
    if ( my $err = $@ ) {
        warn $err;
        return $attr eq 'name' ? 'Unknown user' : 'unknown@localhost';
    }
    my $name = $user->name;
    if ( lc $name eq 'nobody' ) {
        return $attr eq 'name' ? 'nobody' : undef;
    }
    elsif ( lc $name eq 'RT_System' ) {
        return $attr eq 'name' ? 'system' : undef;
    } else {
        return $user->$attr();
    }
}

memoize 'resolve_user_id_to';

our %PROP_MAP = (
    subject         => 'summary',
    status          => 'status',
    owner           => 'owner',
    initialpriority => '_delete',
    finalpriority   => '_delete',
    told            => '_delete',
    requestor       => 'reporter',
    requestors      => 'reporter',
    cc              => 'cc',
    ccs             => 'cc',
    admincc         => 'admin_cc',
    adminccs        => 'admin_cc',
    refersto        => 'refers_to',
    referredtoby    => 'referred_to_by',
    dependson       => 'depends_on',
    dependedonby    => 'depended_on_by',
    hasmember       => 'members',
    memberof        => 'member_of',
    priority        => 'priority_integer',
    resolved        => 'completed',
    due             => 'due',
    creator         => 'creator',
    timeworked      => 'time_worked',
    timeleft        => 'time_left',
    timeestimated   => 'time_estimated',
    lastupdated     => '_delete',
    created         => 'created',
    queue           => 'queue',
    starts          => '_delete',
    started         => '_delete',
);

sub translate_status {
    my $self = shift;
    my $status = shift;

    $status =~ s/^resolved$/closed/;
    

    return $status;
}

sub translate_prop_names {
    my $self      = shift;
    my $changeset = shift;

    for my $change ( $changeset->changes ) {
        next unless $change->record_type eq 'ticket';

        my @new_props;
        for my $prop ( $change->prop_changes ) {
            next if ( ( $PROP_MAP{ lc( $prop->name ) } || '' ) eq '_delete' );
            $prop->name( $PROP_MAP{ lc( $prop->name ) } ) if $PROP_MAP{ lc( $prop->name ) };
            # Normalize away undef -> "" and vice-versa
            for (qw/new_value old_value/) {
                $prop->$_("") if !defined ($prop->$_());
                }
            next if ( $prop->old_value eq $prop->new_value);

            if ( $prop->name =~ /^cf-(.*)$/ ) {
                $prop->name( 'custom-' . $1 );
            }

            push @new_props, $prop;

        }
        $change->prop_changes( \@new_props );

    }
    return $changeset;
}

__PACKAGE__->meta->make_immutable;
no Any::Moose;
1;



( run in 1.020 second using v1.01-cache-2.11-cpan-39bf76dae61 )