[Rt-commit] rt branch, 4.2/serialize-ticket, created. rt-4.2.13-122-gce38161

Shawn Moore shawn at bestpractical.com
Fri Feb 10 17:21:24 EST 2017


The branch, 4.2/serialize-ticket has been created
        at  ce381610a24c7ea1b90f95c64e74b2b0c95b85e6 (commit)

- Log -----------------------------------------------------------------
commit 946461108f16d6008a4195b8db61f52343bf9a13
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Fri Feb 10 20:03:16 2017 +0000

    Add support for exporting individual tickets
    
    This adds a --limit-tickets=1,2 option to rt-serializer

diff --git a/lib/RT/Migrate/Serializer.pm b/lib/RT/Migrate/Serializer.pm
index 3855ee9..d4e412a 100644
--- a/lib/RT/Migrate/Serializer.pm
+++ b/lib/RT/Migrate/Serializer.pm
@@ -91,6 +91,7 @@ sub Init {
                   FollowACL
                   Queues
                   CustomFields
+                  TicketIDs
                   HyperlinkUnmigrated
                   Clone
                   Incremental
@@ -302,7 +303,12 @@ sub PushBasics {
         $self->PushCollections(qw(Topics Classes));
     }
 
-    if ($self->{Queues}) {
+    if ($self->{TicketIDs}) {
+        my $tickets = RT::Tickets->new(RT->SystemUser);
+        $tickets->Limit(FIELD => 'id', OPERATOR => 'IN', VALUE => $self->{TicketIDs});
+        $self->PushObj($tickets);
+    }
+    elsif ($self->{Queues}) {
         my $queues = RT::Queues->new(RT->SystemUser);
         $queues->Limit(FIELD => 'id', OPERATOR => 'IN', VALUE => $self->{Queues});
         $self->PushObj($queues);
@@ -416,8 +422,13 @@ sub Observe {
     my $from = $args{from};
     if ($obj->isa("RT::Ticket")) {
         return 0 if $obj->Status eq "deleted" and not $self->{FollowDeleted};
+
+        my $id = $obj->EffectiveId;
+        return 0 if $self->{TicketIDs} && none { $id == $_ } @{ $self->{TicketIDs} };
+
         my $queue = $obj->Queue;
         return 0 if $self->{Queues} && none { $queue == $_ } @{ $self->{Queues} };
+
         return $self->{FollowTickets};
     } elsif ($obj->isa("RT::Queue")) {
         my $id = $obj->Id;
diff --git a/sbin/rt-serializer.in b/sbin/rt-serializer.in
index c9aa484..e4e3cba 100644
--- a/sbin/rt-serializer.in
+++ b/sbin/rt-serializer.in
@@ -106,6 +106,7 @@ GetOptions(
     "acls!",
     "limit-queues=s@",
     "limit-cfs=s@",
+    "limit-tickets=s@",
     "hyperlink-unmigrated!",
 
     "clone",
@@ -177,6 +178,11 @@ if ($OPT{'limit-cfs'}) {
     $args{CustomFields} = \@cf_ids;
 }
 
+if ($OPT{'limit-tickets'}) {
+    $args{FollowTickets} = 1;
+    $args{TicketIDs} = [ split ',', join ',', @{ $OPT{'limit-tickets'} } ];
+}
+
 if (($OPT{clone} or $OPT{incremental})
         and grep { /^(users|groups|deleted|scrips|tickets|acls)$/ } keys %OPT) {
     die "You cannot specify object types when cloning.\n\nPlease see $0 --help.\n";
@@ -375,6 +381,11 @@ that set of queues (and the tickets in them) will be serialized.
 Takes a list of custom field IDs or names separated by commas. When provided,
 only that set of custom fields will be serialized.
 
+=item B<--limit-tickets>
+
+Takes a list of ticket IDs separated by commas. When provided, only that
+set of tickets will be serialized.
+
 =item B<--hyperlink-unmigrated>
 
 Replace links to local records which are not being migrated with hyperlinks.

commit ca06c92a43357ef8b3e402b75039675236aa58ff
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Fri Feb 10 20:25:26 2017 +0000

    Add --attributes flag to serializer

diff --git a/lib/RT/Migrate/Serializer.pm b/lib/RT/Migrate/Serializer.pm
index d4e412a..e8212ad 100644
--- a/lib/RT/Migrate/Serializer.pm
+++ b/lib/RT/Migrate/Serializer.pm
@@ -71,6 +71,7 @@ sub Init {
         FollowScrips        => 0,
         FollowTickets       => 1,
         FollowACL           => 0,
+        FollowAttributes    => 0,
 
         Clone       => 0,
         Incremental => 0,
@@ -89,6 +90,7 @@ sub Init {
                   FollowScrips
                   FollowTickets
                   FollowACL
+                  FollowAttributes
                   Queues
                   CustomFields
                   TicketIDs
@@ -264,9 +266,11 @@ sub PushBasics {
     $self->PushObj( $cfs );
 
     # Global attributes
-    my $attributes = RT::Attributes->new( RT->SystemUser );
-    $attributes->LimitToObject( $RT::System );
-    $self->PushObj( $attributes );
+    if ($self->{FollowAttributes}) {
+        my $attributes = RT::Attributes->new( RT->SystemUser );
+        $attributes->LimitToObject( $RT::System );
+        $self->PushObj( $attributes );
+    }
 
     # Global ACLs
     if ($self->{FollowACL}) {
@@ -457,6 +461,8 @@ sub Observe {
         } elsif ($grp->Domain eq "SystemInternal") {
             return 0 if $grp->UID eq $from;
         }
+    } elsif ($obj->isa("RT::Attribute")) {
+        return $self->{FollowAttributes};
     }
 
     return 1;
diff --git a/sbin/rt-serializer.in b/sbin/rt-serializer.in
index e4e3cba..5c1aa34 100644
--- a/sbin/rt-serializer.in
+++ b/sbin/rt-serializer.in
@@ -104,6 +104,7 @@ GetOptions(
     "scrips!",
     "tickets!",
     "acls!",
+    "attributes!",
     "limit-queues=s@",
     "limit-cfs=s@",
     "limit-tickets=s@",
@@ -127,9 +128,10 @@ $args{AllUsers}      = $OPT{users}    if defined $OPT{users};
 $args{AllGroups}     = $OPT{groups}   if defined $OPT{groups};
 $args{FollowDeleted} = $OPT{deleted}  if defined $OPT{deleted};
 
-$args{FollowScrips}  = $OPT{scrips}   if defined $OPT{scrips};
-$args{FollowTickets} = $OPT{tickets}  if defined $OPT{tickets};
-$args{FollowACL}     = $OPT{acls}     if defined $OPT{acls};
+$args{FollowScrips}     = $OPT{scrips}     if defined $OPT{scrips};
+$args{FollowTickets}    = $OPT{tickets}    if defined $OPT{tickets};
+$args{FollowACL}        = $OPT{acls}       if defined $OPT{acls};
+$args{FollowAttributes} = $OPT{attributes} if defined $OPT{attributes};
 
 $args{HyperlinkUnmigrated} = $OPT{'hyperlink-unmigrated'} if defined $OPT{'hyperlink-unmigrated'};
 
@@ -184,7 +186,7 @@ if ($OPT{'limit-tickets'}) {
 }
 
 if (($OPT{clone} or $OPT{incremental})
-        and grep { /^(users|groups|deleted|scrips|tickets|acls)$/ } keys %OPT) {
+        and grep { /^(users|groups|deleted|scrips|tickets|acls|attributes)$/ } keys %OPT) {
     die "You cannot specify object types when cloning.\n\nPlease see $0 --help.\n";
 }
 
@@ -367,6 +369,11 @@ scrips and templates to be serialized.
 No ACLs are serialized by default; this option forces all ACLs to be
 serialized.
 
+=item B<--attributes>
+
+No attributes (which include saved searches, dashboards) are serialized
+by default; this option forces all attributes to be serialized.
+
 =item B<--no-tickets>
 
 Skip serialization of all ticket data.

commit ce381610a24c7ea1b90f95c64e74b2b0c95b85e6
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Fri Feb 10 21:49:16 2017 +0000

    Add way to follow renames (and existing objects) across imports
    
    rt-importer --follow-renames

diff --git a/lib/RT/Migrate/Importer.pm b/lib/RT/Migrate/Importer.pm
index c0faade..4fad760 100644
--- a/lib/RT/Migrate/Importer.pm
+++ b/lib/RT/Migrate/Importer.pm
@@ -71,6 +71,7 @@ sub Init {
         DumpObjects         => undef,
         HandleError         => undef,
         ExcludeOrganization => undef,
+        FollowRenames       => undef,
         @_,
     );
 
@@ -78,6 +79,7 @@ sub Init {
     $self->{OriginalId} = $args{OriginalId};
 
     $self->{ExcludeOrganization} = $args{ExcludeOrganization};
+    $self->{FollowRenames} = $args{FollowRenames};
 
     $self->{Progress} = $args{Progress};
 
@@ -294,11 +296,15 @@ sub MergeBy {
 
 sub Qualify {
     my $self = shift;
-    my ($string) = @_;
-    return $string if $self->{Clone};
-    return $string if not defined $self->{Organization};
-    return $string if $self->{ExcludeOrganization};
-    return $string if $self->{Organization} eq $RT::Organization;
+    my ($string, $force) = @_;
+
+    if (!$force) {
+        return $string if $self->{Clone};
+        return $string if not defined $self->{Organization};
+        return $string if $self->{ExcludeOrganization};
+        return $string if $self->{Organization} eq $RT::Organization;
+    }
+
     return $self->{Organization}.": $string";
 }
 
@@ -344,6 +350,42 @@ sub Create {
     return $obj;
 }
 
+sub LoadForReuse {
+    my $self = shift;
+    my ($class, $uid) = @_;
+
+    return if grep { $class eq $_ } 'RT::Transaction', 'RT::Attribute';
+
+    my $attribute = RT::Attribute->new( RT->SystemUser );
+    $attribute->LoadByCols(
+        Name       => 'ImporterOrigin',
+        ObjectType => $class,
+        Content    => $self->Qualify($uid, 1),
+    );
+    if (!$attribute->Id) {
+        return;
+    }
+
+    my $obj = $attribute->Object;
+    return $obj;
+}
+
+sub RegisterForReuse {
+    my $self = shift;
+    my ($obj, $uid) = @_;
+
+    return if grep { ref($obj) eq $_ } 'RT::Transaction', 'RT::Attribute';
+    return if $self->LoadForReuse(ref($obj), $uid);
+
+    my $attr = RT::Attribute->new(RT->SystemUser);
+    $attr->Create(
+        Object      => $obj,
+        Name        => 'ImporterOrigin',
+        Content     => $self->Qualify($uid, 1),
+        ContentType => 'text/plain',
+    );
+}
+
 sub ReadStream {
     my $self = shift;
     my ($fh) = @_;
@@ -400,31 +442,47 @@ sub ReadStream {
         if $class eq "RT::Queue";
 
     my $origid = $data->{id};
-    my $obj = $self->Create( $class, $uid, $data );
-    return unless $obj;
+    my $obj;
 
-    # If it's a ticket, we might need to create a
-    # TicketCustomField for the previous ID
-    if ($class eq "RT::Ticket" and $self->{OriginalId}) {
-        my $value = $self->{ExcludeOrganization}
-                  ? $origid
-                  : $self->Organization . ":$origid";
-
-        my ($id, $msg) = $obj->AddCustomFieldValue(
-            Field             => $self->{OriginalId},
-            Value             => $value,
-            RecordTransaction => 0,
-        );
-        warn "Failed to add custom field to $uid: $msg"
-            unless $id;
+    if ($self->{FollowRenames}) {
+        $obj = $self->LoadForReuse( $class, $uid );
+        if ($obj) {
+            $self->Resolve( $uid => $class => $obj->Id );
+            #$self->MergeValues( $obj, $data );
+        }
     }
 
-    # If it's a CF, we don't know yet if it's global (the OCF
-    # hasn't been created yet) to store away the CF for later
-    # inspection
-    push @{$self->{NewCFs}}, $uid
-        if $class eq "RT::CustomField"
-            and $obj->LookupType =~ /^RT::Queue/;
+    if (!$obj) {
+        $obj = $self->Create( $class, $uid, $data );
+        return unless $obj;
+
+        if ($self->{FollowRenames}) {
+            $self->RegisterForReuse( $obj, $uid );
+        }
+
+        # If it's a ticket, we might need to create a
+        # TicketCustomField for the previous ID
+        if ($class eq "RT::Ticket" and $self->{OriginalId}) {
+            my $value = $self->{ExcludeOrganization}
+                      ? $origid
+                      : $self->Organization . ":$origid";
+
+            my ($id, $msg) = $obj->AddCustomFieldValue(
+                Field             => $self->{OriginalId},
+                Value             => $value,
+                RecordTransaction => 0,
+            );
+            warn "Failed to add custom field to $uid: $msg"
+                unless $id;
+        }
+
+        # If it's a CF, we don't know yet if it's global (the OCF
+        # hasn't been created yet) to store away the CF for later
+        # inspection
+        push @{$self->{NewCFs}}, $uid
+            if $class eq "RT::CustomField"
+                and $obj->LookupType =~ /^RT::Queue/;
+    }
 
     $self->{Progress}->($obj) if $self->{Progress};
 }
diff --git a/lib/RT/Migrate/Importer/File.pm b/lib/RT/Migrate/Importer/File.pm
index cf3362b..04eed1b 100644
--- a/lib/RT/Migrate/Importer/File.pm
+++ b/lib/RT/Migrate/Importer/File.pm
@@ -192,7 +192,7 @@ sub SaveState {
            NewQueues NewCFs
            SkipTransactions Pending Invalid
            UIDs
-           OriginalId ExcludeOrganization Clone
+           OriginalId ExcludeOrganization FollowRenames Clone
           /;
     Storable::nstore(\%data, $self->{Statefile});
 
diff --git a/sbin/rt-importer.in b/sbin/rt-importer.in
index 7c957ec..86338c8 100644
--- a/sbin/rt-importer.in
+++ b/sbin/rt-importer.in
@@ -96,6 +96,7 @@ GetOptions(
     "resume!",
     "originalid|i=s",
     "exclude-organization",
+    "follow-renames",
 
     "ask",
     "ignore-errors",
@@ -146,6 +147,7 @@ my $import = RT::Migrate::Importer::File->new(
     Directory           => $dir,
     OriginalId          => $OPT{originalid},
     ExcludeOrganization => $OPT{'exclude-organization'},
+    FollowRenames       => $OPT{'follow-renames'},
     DumpObjects         => $OPT{dump},
     Resume              => $OPT{resume},
     HandleError         => $error_handler,
@@ -252,6 +254,16 @@ Ordinarily certain records (groups, queues, the B<--originalid> custom field)
 include the organization name of the original RT instance. Use this option to
 suppress that behavior and use the original name directly.
 
+=item B<--follow-renames>
+
+This advises rt-importer to try harder to reuse previously-imported
+records, such as custom fields, instead of creating new ones. This
+requires the importer to do more work for each record and so has
+performance penalty. This is intended for sites that frequently import
+small collections.
+
+This is not meant to be used with C<--clone> or C<--incremental>.
+
 =item B<--ask>
 
 Prompt for action when an error occurs inserting a record into the

-----------------------------------------------------------------------


More information about the rt-commit mailing list