private void ProcessWorkItem(WorkItemData sourceWorkItem, int retryLimit = 5, int retrys = 0) { var witstopwatch = Stopwatch.StartNew(); var starttime = DateTime.Now; processWorkItemMetrics = new Dictionary <string, double>(); processWorkItemParamiters = new Dictionary <string, string>(); AddParameter("SourceURL", processWorkItemParamiters, Engine.Source.WorkItems.Config.Collection.ToString()); AddParameter("SourceWorkItem", processWorkItemParamiters, sourceWorkItem.Id.ToString()); AddParameter("TargetURL", processWorkItemParamiters, Engine.Target.WorkItems.Config.Collection.ToString()); AddParameter("TargetProject", processWorkItemParamiters, Engine.Target.WorkItems.Project.Name); AddParameter("RetryLimit", processWorkItemParamiters, retryLimit.ToString()); AddParameter("RetryNumber", processWorkItemParamiters, retrys.ToString()); try { if (sourceWorkItem.Type != "Test Plan" || sourceWorkItem.Type != "Test Suite") { var targetWorkItem = Engine.Target.WorkItems.FindReflectedWorkItem(sourceWorkItem, false); /////////////////////////////////////////////// TraceWriteLine(LogEventLevel.Information, "Work Item has {sourceWorkItemRev} revisions and revision migration is set to {ReplayRevisions}", new Dictionary <string, object>() { { "sourceWorkItemRev", sourceWorkItem.Rev }, { "ReplayRevisions", _config.ReplayRevisions } } ); List <RevisionItem> revisionsToMigrate = RevisionsToMigrate(sourceWorkItem, targetWorkItem); if (targetWorkItem == null) { targetWorkItem = ReplayRevisions(revisionsToMigrate, sourceWorkItem, null, _current); AddMetric("Revisions", processWorkItemMetrics, revisionsToMigrate.Count); } else { if (revisionsToMigrate.Count == 0) { ProcessWorkItemAttachments(sourceWorkItem, targetWorkItem, false); ProcessWorkItemLinks(Engine.Source.WorkItems, Engine.Target.WorkItems, sourceWorkItem, targetWorkItem); TraceWriteLine(LogEventLevel.Information, "Skipping as work item exists and no revisions to sync detected"); processWorkItemMetrics.Add("Revisions", 0); } else { TraceWriteLine(LogEventLevel.Information, "Syncing as there are {revisionsToMigrateCount} revisons detected", new Dictionary <string, object>() { { "revisionsToMigrateCount", revisionsToMigrate.Count } }); targetWorkItem = ReplayRevisions(revisionsToMigrate, sourceWorkItem, targetWorkItem, _current); AddMetric("Revisions", processWorkItemMetrics, revisionsToMigrate.Count); AddMetric("SyncRev", processWorkItemMetrics, revisionsToMigrate.Count); } } AddParameter("TargetWorkItem", processWorkItemParamiters, targetWorkItem.ToWorkItem().Revisions.Count.ToString()); /////////////////////////////////////////////// ProcessHTMLFieldAttachements(targetWorkItem); /////////////////////////////////////////////// /////////////////////////////////////////////////////// if (targetWorkItem != null && targetWorkItem.ToWorkItem().IsDirty) { targetWorkItem.SaveToAzureDevOps(); } if (targetWorkItem != null) { targetWorkItem.ToWorkItem().Close(); } if (sourceWorkItem != null) { sourceWorkItem.ToWorkItem().Close(); } } else { TraceWriteLine(LogEventLevel.Warning, "SKIP: Unable to migrate {sourceWorkItemTypeName}/{sourceWorkItemId}. Use the TestPlansAndSuitesMigrationContext after you have migrated all Test Cases. ", new Dictionary <string, object>() { { "sourceWorkItemTypeName", sourceWorkItem.Type }, { "sourceWorkItemId", sourceWorkItem.Id } }); } } catch (WebException ex) { Log.Error(ex, "Some kind of internet pipe blockage"); if (retrys < retryLimit) { TraceWriteLine(LogEventLevel.Warning, "WebException: Will retry in {retrys}s ", new Dictionary <string, object>() { { "retrys", retrys } }); System.Threading.Thread.Sleep(new TimeSpan(0, 0, retrys)); retrys++; TraceWriteLine(LogEventLevel.Warning, "RETRY {Retrys}/{RetryLimit} ", new Dictionary <string, object>() { { "Retrys", retrys }, { "RetryLimit", retryLimit } }); ProcessWorkItem(sourceWorkItem, retryLimit, retrys); } else { TraceWriteLine(LogEventLevel.Error, "ERROR: Failed to create work item. Retry Limit reached "); } } catch (Exception ex) { Log.Error(ex, ex.ToString()); Telemetry.TrackRequest("ProcessWorkItem", starttime, witstopwatch.Elapsed, "502", false); throw ex; } witstopwatch.Stop(); _elapsedms += witstopwatch.ElapsedMilliseconds; processWorkItemMetrics.Add("ElapsedTimeMS", _elapsedms); var average = new TimeSpan(0, 0, 0, 0, (int)(_elapsedms / _current)); var remaining = new TimeSpan(0, 0, 0, 0, (int)(average.TotalMilliseconds * _count)); TraceWriteLine(LogEventLevel.Information, "Average time of {average:%s}.{average:%fff} per work item and {remaining:%h} hours {remaining:%m} minutes {remaining:%s}.{remaining:%fff} seconds estimated to completion", new Dictionary <string, object>() { { "average", average }, { "remaining", remaining } }); Trace.Flush(); Telemetry.TrackEvent("WorkItemMigrated", processWorkItemParamiters, processWorkItemMetrics); Telemetry.TrackRequest("ProcessWorkItem", starttime, witstopwatch.Elapsed, "200", true); _current++; _count--; }