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 )