internal override void InternalExecute() { Stopwatch stopwatch = Stopwatch.StartNew(); ////////////////////////////////////////////////// WorkItemStoreContext targetStore = new WorkItemStoreContext(me.Target, WorkItemStoreFlags.BypassRules); TfsQueryContext tfsqc = new TfsQueryContext(targetStore); TfsTeamService teamService = me.Target.Collection.GetService <TfsTeamService>(); QueryHierarchy qh = targetStore.Store.Projects[me.Target.Config.Project].QueryHierarchy; List <TeamFoundationTeam> teamList = teamService.QueryTeams(me.Target.Config.Project).ToList(); Trace.WriteLine(string.Format("Found {0} teams?", teamList.Count)); ////////////////////////////////////////////////// int current = teamList.Count; int count = 0; long elapsedms = 0; foreach (TeamFoundationTeam team in teamList) { Stopwatch witstopwatch = Stopwatch.StartNew(); Trace.Write(string.Format("Processing team {0}", team.Name)); Regex r = new Regex(@"^Project - ([a-zA-Z ]*)"); string path; if (r.IsMatch(team.Name)) { Trace.Write(string.Format(" is a Project")); path = string.Format(@"Projects\{0}", r.Match(team.Name).Groups[1].Value.Replace(" ", "-")); } else { Trace.Write(string.Format(" is a Team")); path = string.Format(@"Teams\{0}", team.Name.Replace(" ", "-")); } Trace.Write(string.Format(" and new path is {0}", path)); //me.AddFieldMap("*", new RegexFieldMap("KM.Simulation.Team", "System.AreaPath", @"^Project - ([a-zA-Z ]*)", @"Nemo\Projects\$1")); string[] bits = path.Split(char.Parse(@"\")); CreateFolderHyerarchy(bits, qh["Shared Queries"]); //_me.ApplyFieldMappings(workitem); qh.Save(); witstopwatch.Stop(); elapsedms = elapsedms + witstopwatch.ElapsedMilliseconds; current--; count++; TimeSpan average = new TimeSpan(0, 0, 0, 0, (int)(elapsedms / count)); TimeSpan remaining = new TimeSpan(0, 0, 0, 0, (int)(average.TotalMilliseconds * current)); Trace.WriteLine(""); //Trace.WriteLine(string.Format("Average time of {0} per work item and {1} estimated to completion", string.Format(@"{0:s\:fff} seconds", average), string.Format(@"{0:%h} hours {0:%m} minutes {0:s\:fff} seconds", remaining))); } ////////////////////////////////////////////////// stopwatch.Stop(); Console.WriteLine(@"DONE in {0:%h} hours {0:%m} minutes {0:s\:fff} seconds", stopwatch.Elapsed); }
protected override void InternalExecute() { Stopwatch stopwatch = Stopwatch.StartNew(); ////////////////////////////////////////////////// TfsTeamService teamService = Engine.Target.GetService <TfsTeamService>(); QueryHierarchy qh = ((TfsWorkItemMigrationClient)Engine.Target.WorkItems).Store.Projects[Engine.Target.Config.AsTeamProjectConfig().Project].QueryHierarchy; List <TeamFoundationTeam> teamList = teamService.QueryTeams(Engine.Target.Config.AsTeamProjectConfig().Project).ToList(); Log.LogInformation("Found {0} teams?", teamList.Count); ////////////////////////////////////////////////// int current = teamList.Count; int count = 0; long elapsedms = 0; foreach (TeamFoundationTeam team in teamList) { Stopwatch witstopwatch = Stopwatch.StartNew(); Log.LogTrace("Processing team {0}", team.Name); Regex r = new Regex(@"^Project - ([a-zA-Z ]*)"); string path; if (r.IsMatch(team.Name)) { Log.LogInformation("{0} is a Project", team.Name); path = string.Format(@"Projects\{0}", r.Match(team.Name).Groups[1].Value.Replace(" ", "-")); } else { Log.LogInformation("{0} is a Team", team.Name); path = string.Format(@"Teams\{0}", team.Name.Replace(" ", "-")); } Log.LogInformation(" and new path is {0}", path); //me.AddFieldMap("*", new RegexFieldMap("KM.Simulation.Team", "System.AreaPath", @"^Project - ([a-zA-Z ]*)", @"Nemo\Projects\$1")); string[] bits = path.Split(char.Parse(@"\")); CreateFolderHyerarchy(bits, qh["Shared Queries"]); //_me.ApplyFieldMappings(workitem); qh.Save(); witstopwatch.Stop(); elapsedms = elapsedms + witstopwatch.ElapsedMilliseconds; current--; count++; TimeSpan average = new TimeSpan(0, 0, 0, 0, (int)(elapsedms / count)); TimeSpan remaining = new TimeSpan(0, 0, 0, 0, (int)(average.TotalMilliseconds * current)); Log.LogInformation("Average time of {average} per work item and {remaining} estimated to completion", average.ToString("c"), remaining.ToString("c")); } ////////////////////////////////////////////////// stopwatch.Stop(); Log.LogInformation("DONE in {Elapsed} ", stopwatch.Elapsed.ToString("c")); }
/// <summary> /// Add Query Definition under a specific Query Folder. /// </summary> /// <param name="targetHierarchy">The object that represents the whole of the target query tree</param> /// <param name="query">Query Definition - Contains the Query Details</param> /// <param name="QueryFolder">Parent Folder</param> void MigrateQuery(QueryHierarchy targetHierarchy, QueryDefinition query, QueryFolder parentFolder) { if (parentFolder.FirstOrDefault(q => q.Name == query.Name) != null) { this.totalQueriesSkipped++; Trace.WriteLine($"Skipping query '{query.Name}' as already exists"); } else { // Sort out any path issues in the quertText var fixedQueryText = query.QueryText.Replace($"'{Engine.Source.Config.Project}", $"'{Engine.Target.Config.Project}"); // the ' should only items at the start of areapath etc. if (config.PrefixProjectToNodes) { // we need to inject the team name as a folder in the structure too fixedQueryText = fixedQueryText.Replace($"{Engine.Target.Config.Project}\\", $"{Engine.Target.Config.Project}\\{Engine.Source.Config.Project}\\"); } if (config.SourceToTargetFieldMappings != null) { foreach (var sourceField in config.SourceToTargetFieldMappings.Keys) { fixedQueryText = query.QueryText.Replace($"{sourceField}", $"'{config.SourceToTargetFieldMappings[sourceField]}"); } } // you cannot just add an item from one store to another, we need to create a new object var queryCopy = new QueryDefinition(query.Name, fixedQueryText); this.totalQueriesAttempted++; Trace.WriteLine($"Migrating query '{query.Name}'"); parentFolder.Add(queryCopy); try { targetHierarchy.Save(); // moved the save here for better error message this.totalQueriesMigrated++; } catch (Exception ex) { this.totalQueryFailed++; Trace.WriteLine($"Error saving query '{query.Name}', probably due to invalid area or iteration paths"); Trace.WriteLine($"Source Query: '{query}'"); Trace.WriteLine($"Target Query: '{fixedQueryText}'"); Trace.WriteLine(ex.Message); targetHierarchy.Refresh(); // get the tree without the last edit } } }
/// <summary> /// Define Query Folders under the current parent /// </summary> /// <param name="targetHierarchy">The object that represents the whole of the target query tree</param> /// <param name="sourceFolder">The source folder in tree on source instance</param> /// <param name="parentFolder">The target folder in tree on target instance</param> private void MigrateFolder(QueryHierarchy targetHierarchy, QueryFolder sourceFolder, QueryFolder parentFolder) { // We only migrate non-private folders and their contents if (sourceFolder.IsPersonal) { Log.LogInformation("Found a personal folder {sourceFolderName}. Migration only available for shared Team Query folders", sourceFolder.Name); } else { this.totalFoldersAttempted++; // we need to replace the team project name in folder names as it included in query paths var requiredPath = sourceFolder.Path.Replace($"{Source.Project}/", $"{Target.Project}/"); // Is the project name to be used in the migration as an extra folder level? if (_Options.PrefixProjectToNodes == true) { // we need to inject the team name as a folder in the structure requiredPath = requiredPath.Replace(_Options.SharedFolderName, $"{_Options.SharedFolderName}/{Source.Project}"); // If on the root level we need to check that the extra folder has already been added if (sourceFolder.Path.Count(f => f == '/') == 1) { var targetSharedFolderRoot = (QueryFolder)parentFolder[_Options.SharedFolderName]; QueryFolder extraFolder = (QueryFolder)targetSharedFolderRoot.FirstOrDefault(q => q.Path == requiredPath); if (extraFolder == null) { // we are at the root level on the first pass and need to create the extra folder for the team name Log.LogInformation("Adding a folder '{Project}'", Source.Project); extraFolder = new QueryFolder(Source.Project); targetSharedFolderRoot.Add(extraFolder); targetHierarchy.Save(); // moved the save here a more immediate and relavent error message } // adjust the working folder to the newly added one parentFolder = targetSharedFolderRoot; } } // check if there is a folder of the required name, using the path to make sure it is unique QueryFolder targetFolder = (QueryFolder)parentFolder.FirstOrDefault(q => q.Path == requiredPath); if (targetFolder != null) { Log.LogInformation("Skipping folder '{sourceFolderName}' as already exists", sourceFolder.Name); } else { Log.LogInformation("Migrating a folder '{sourceFolderName}'", sourceFolder.Name); targetFolder = new QueryFolder(sourceFolder.Name); parentFolder.Add(targetFolder); targetHierarchy.Save(); // moved the save here a more immediate and relavent error message } // Process child items foreach (QueryItem sub_query in sourceFolder) { if (sub_query.GetType() == typeof(QueryFolder)) { MigrateFolder(targetHierarchy, (QueryFolder)sub_query, (QueryFolder)targetFolder); } else { MigrateQuery(targetHierarchy, (QueryDefinition)sub_query, (QueryFolder)targetFolder); } } } }