1111using Migration . Common ;
1212using Migration . Common . Log ;
1313using Migration . WIContract ;
14+ using WorkItemImport . WitClient ;
1415
1516namespace WorkItemImport
1617{
@@ -25,9 +26,9 @@ public WitClientUtils(IWitClientWrapper witClientWrapper)
2526
2627 public delegate V IsAttachmentMigratedDelegate < in T , U , out V > ( T input , out U output ) ;
2728
28- public WorkItem CreateWorkItem ( string type )
29+ public WorkItem CreateWorkItem ( string type , DateTime ? createdDate = null , string createdBy = "" )
2930 {
30- return _witClientWrapper . CreateWorkItem ( type ) ;
31+ return _witClientWrapper . CreateWorkItem ( type , createdDate , createdBy ) ;
3132 }
3233
3334 public bool IsDuplicateWorkItemLink ( IEnumerable < WorkItemRelation > links , WorkItemRelation relatedLink )
@@ -202,12 +203,17 @@ public void EnsureDateFields(WiRevision rev, WorkItem wi)
202203 }
203204 if ( ! rev . Fields . HasAnyByRefName ( WiFieldReference . ChangedDate ) )
204205 {
205- if ( DateTime . Parse ( wi . Fields [ WiFieldReference . ChangedDate ] . ToString ( ) ) == rev . Time )
206+ DateTime workItemChangedDate = ( DateTime ) ( wi . Fields [ WiFieldReference . ChangedDate ] ) ;
207+ if ( workItemChangedDate . ToUniversalTime ( ) == rev . Time . ToUniversalTime ( ) )
206208 {
207209 rev . Fields . Add ( new WiField ( ) { ReferenceName = WiFieldReference . ChangedDate , Value = rev . Time . AddMilliseconds ( 1 ) . ToString ( "o" ) } ) ;
208210 }
209211 else
210- rev . Fields . Add ( new WiField ( ) { ReferenceName = WiFieldReference . ChangedDate , Value = rev . Time . ToString ( "o" ) } ) ;
212+ {
213+ // ADO can add a few milliseconds to work item createdDate when adding an attachment, hence adding more here to the revision time
214+ rev . Fields . Add ( new WiField ( ) { ReferenceName = WiFieldReference . ChangedDate , Value = rev . Time . AddMilliseconds ( 5 ) . ToString ( "o" ) } ) ;
215+ wi . Fields [ WiFieldReference . ChangedDate ] = rev . Time . AddMilliseconds ( 5 ) ;
216+ }
211217 }
212218
213219 }
@@ -450,16 +456,27 @@ public void SaveWorkItemAttachments(WiRevision rev, WorkItem wi)
450456 throw new ArgumentException ( nameof ( wi ) ) ;
451457 }
452458
459+ // Calculate UpdatedTime
460+ DateTime attachmentUpdatedDate = rev . Time ;
461+ DateTime workItemChangedDate = ( DateTime ) wi . Fields [ WiFieldReference . ChangedDate ] ;
462+ if ( workItemChangedDate . ToUniversalTime ( ) > rev . Time . ToUniversalTime ( ) )
463+ {
464+ attachmentUpdatedDate = workItemChangedDate ;
465+ // The work item ChangeDate is altered when saving the attachment, make sure the Revision time does too.
466+ // Otherwise it will not be an increased ChangedDate and we'll get an exception
467+ rev . Time = workItemChangedDate ;
468+ }
469+
453470 // Save attachments
454471 foreach ( WiAttachment attachment in rev . Attachments )
455472 {
456473 if ( attachment . Change == ReferenceChangeType . Added )
457474 {
458- AddSingleAttachmentToWorkItemAndSave ( attachment , wi ) ;
475+ AddSingleAttachmentToWorkItemAndSave ( attachment , wi , attachmentUpdatedDate , rev . Author ) ;
459476 }
460477 else if ( attachment . Change == ReferenceChangeType . Removed )
461478 {
462- RemoveSingleAttachmentFromWorkItemAndSave ( attachment , wi ) ;
479+ RemoveSingleAttachmentFromWorkItemAndSave ( attachment , wi , attachmentUpdatedDate , rev . Author ) ;
463480 }
464481 }
465482 }
@@ -476,23 +493,16 @@ public void SaveWorkItemFields(WorkItem wi)
476493 foreach ( string key in wi . Fields . Keys )
477494 {
478495 if ( new string [ ] {
479- WiFieldReference . ChangedDate ,
480496 WiFieldReference . BoardColumn ,
481497 WiFieldReference . BoardColumnDone ,
482498 WiFieldReference . BoardLane ,
483499 } . Contains ( key ) )
484500 continue ;
485501
486-
487502 object val = wi . Fields [ key ] ;
488503
489504 patchDocument . Add (
490- new JsonPatchOperation ( )
491- {
492- Operation = Operation . Add ,
493- Path = "/fields/" + key ,
494- Value = val
495- }
505+ JsonPatchDocUtils . CreateJsonFieldPatchOp ( Operation . Add , key , val )
496506 ) ;
497507 }
498508
@@ -593,7 +603,7 @@ private void CorrectActivatedByAndActivatedDate(WiRevision rev, WorkItem wi)
593603 }
594604 }
595605
596- private void AddSingleAttachmentToWorkItemAndSave ( WiAttachment att , WorkItem wi )
606+ private void AddSingleAttachmentToWorkItemAndSave ( WiAttachment att , WorkItem wi , DateTime ? changedDate = null , string changedBy = "" )
597607 {
598608 // Upload attachment
599609 AttachmentReference attachment = _witClientWrapper . CreateAttachment ( att ) ;
@@ -621,6 +631,30 @@ private void AddSingleAttachmentToWorkItemAndSave(WiAttachment att, WorkItem wi)
621631 }
622632 } ;
623633
634+ if ( changedDate != null )
635+ {
636+ DateTime workItemChangedDate = ( DateTime ) wi . Fields [ WiFieldReference . ChangedDate ] ;
637+ if ( changedDate . Value . ToUniversalTime ( ) >= workItemChangedDate . ToUniversalTime ( ) )
638+ {
639+ attachmentPatchDocument . Add (
640+ JsonPatchDocUtils . CreateJsonFieldPatchOp ( Operation . Add , WiFieldReference . ChangedDate , changedDate )
641+ ) ;
642+ }
643+ else
644+ {
645+ attachmentPatchDocument . Add (
646+ JsonPatchDocUtils . CreateJsonFieldPatchOp ( Operation . Add , WiFieldReference . ChangedDate , workItemChangedDate . AddMilliseconds ( 1 ) )
647+ ) ;
648+ }
649+ }
650+
651+ if ( ! string . IsNullOrEmpty ( changedBy ) )
652+ {
653+ attachmentPatchDocument . Add (
654+ JsonPatchDocUtils . CreateJsonFieldPatchOp ( Operation . Add , WiFieldReference . ChangedBy , changedBy )
655+ ) ;
656+ }
657+
624658 var attachments = wi . Relations ? . Where ( r => r . Rel == "AttachedFile" ) ?? new List < WorkItemRelation > ( ) ;
625659 var previousAttachmentsCount = attachments . Count ( ) ;
626660
@@ -637,9 +671,12 @@ private void AddSingleAttachmentToWorkItemAndSave(WiAttachment att, WorkItem wi)
637671 Logger . Log ( LogLevel . Info , "" ) ;
638672
639673 wi . Relations = result . Relations ;
674+
675+ // While updating the work item, the changed date can be increased, hence we take it over
676+ wi . Fields [ WiFieldReference . ChangedDate ] = result . Fields [ WiFieldReference . ChangedDate ] ;
640677 }
641678
642- private void RemoveSingleAttachmentFromWorkItemAndSave ( WiAttachment att , WorkItem wi )
679+ private void RemoveSingleAttachmentFromWorkItemAndSave ( WiAttachment att , WorkItem wi , DateTime changedDate = default , string changedBy = default )
643680 {
644681 WorkItemRelation existingAttachmentRelation =
645682 wi . Relations ? . SingleOrDefault (
@@ -669,6 +706,20 @@ private void RemoveSingleAttachmentFromWorkItemAndSave(WiAttachment att, WorkIte
669706 }
670707 } ;
671708
709+ if ( changedDate != default )
710+ {
711+ attachmentPatchDocument . Add (
712+ JsonPatchDocUtils . CreateJsonFieldPatchOp ( Operation . Add , WiFieldReference . ChangedDate , changedDate )
713+ ) ;
714+ }
715+
716+ if ( changedBy != default )
717+ {
718+ attachmentPatchDocument . Add (
719+ JsonPatchDocUtils . CreateJsonFieldPatchOp ( Operation . Add , WiFieldReference . ChangedBy , changedBy )
720+ ) ;
721+ }
722+
672723 IEnumerable < WorkItemRelation > existingAttachments = wi . Relations ? . Where ( r => r . Rel == "AttachedFile" ) ?? new List < WorkItemRelation > ( ) ;
673724 int previousAttachmentsCount = existingAttachments . Count ( ) ;
674725
0 commit comments