using Adaptation.FileHandlers.ConvertExcelToJson; using Adaptation.FileHandlers.json.WorkItems; using Adaptation.Shared; using Adaptation.Shared.Duplicator; using Adaptation.Shared.Methods; using Microsoft.TeamFoundation.WorkItemTracking.WebApi; using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; using Microsoft.VisualStudio.Services.WebApi.Patch; using Microsoft.VisualStudio.Services.WebApi.Patch.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; namespace Adaptation.FileHandlers.json; public class ProcessData : IProcessData { private readonly List _Details; List Shared.Properties.IProcessData.Details => _Details; public ProcessData(IFileRead fileRead, Logistics logistics, List fileInfoCollection, HttpClient httpClient, string basePage, string api, string query, WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, ReadOnlyDictionary assignedToNameToEncodedPAT, ReadOnlyDictionary assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, string json, bool forceUpdatedBy, bool forceDeleteUpdatedBy) { fileInfoCollection.Clear(); _Details = new List(); Parse(fileRead, logistics, fileInfoCollection, httpClient, basePage, api, query, workItemTrackingHttpClient, project, assignedToNameToEncodedPAT, assignedToNameToUser, requestorNameToUser, json, forceUpdatedBy, forceDeleteUpdatedBy); } string IProcessData.GetCurrentReactor(IFileRead fileRead, Logistics logistics, Dictionary reactors) => throw new Exception(string.Concat("See ", nameof(Parse))); Tuple> IProcessData.GetResults(IFileRead fileRead, Logistics logistics, List fileInfoCollection) => new(logistics.Logistics1[0], Array.Empty(), Array.Empty(), fileInfoCollection); internal static List GetDescriptions(JsonElement[] jsonElements) => throw new NotImplementedException(); #nullable enable private static void AddPatch(JsonPatchDocument document, string path, object value) => document.Add(new JsonPatchOperation { From = null, Operation = Operation.Add, Path = path, Value = value }); private static Dictionary GetFIBacklogMesaCollection(string json) { Dictionary results = new(); string key; FIBacklogMesa[]? fiBacklogMesaCollection; fiBacklogMesaCollection = JsonSerializer.Deserialize(json, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); if (fiBacklogMesaCollection is null || fiBacklogMesaCollection.Length == 0) throw new NullReferenceException(); foreach (FIBacklogMesa fiBacklogMesa in fiBacklogMesaCollection) { if (string.IsNullOrEmpty(fiBacklogMesa.Req)) continue; if (string.IsNullOrEmpty(fiBacklogMesa.Submitted)) continue; if (string.IsNullOrEmpty(fiBacklogMesa.Requestor)) continue; key = $"{fiBacklogMesa.Req} - "; if (results.ContainsKey(key)) continue; results.Add(key, fiBacklogMesa); } return results; } private static string GetIds(HttpClient httpClient, string basePage, string api, string query) { StringBuilder result = new(); Task httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, query)); httpResponseMessageTask.Wait(); if (!httpResponseMessageTask.Result.IsSuccessStatusCode) throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString()); Task streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync(); streamTask.Wait(); if (!streamTask.Result.CanRead) throw new NullReferenceException(nameof(streamTask)); WIQL.Root? root = JsonSerializer.Deserialize(streamTask.Result, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); streamTask.Result.Dispose(); if (root is null || root.WorkItems is null) throw new NullReferenceException(nameof(root)); foreach (WIQL.WorkItem workItem in root.WorkItems) _ = result.Append(workItem.Id).Append(','); if (result.Length > 0) _ = result.Remove(result.Length - 1, 1); return result.ToString(); } private static Value[] GetWorkItems(IFileRead fileRead, HttpClient httpClient, string basePage, string api, string ids) { Value[]? results; Task httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems?ids={ids}")); httpResponseMessageTask.Wait(); if (!httpResponseMessageTask.Result.IsSuccessStatusCode) throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString()); Task streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync(); streamTask.Wait(); if (!streamTask.Result.CanRead) throw new NullReferenceException(nameof(streamTask)); JsonElement? jsonElement = JsonSerializer.Deserialize(streamTask.Result); if (jsonElement is null || jsonElement.Value.ValueKind != JsonValueKind.Object) throw new NullReferenceException(nameof(jsonElement)); if (!fileRead.IsEAFHosted) File.WriteAllText("../../example.json", jsonElement.Value.EnumerateObject().Last().Value.ToString()); results = JsonSerializer.Deserialize(jsonElement.Value.EnumerateObject().Last().Value); if (results is null || results.Length == 0) throw new NullReferenceException(nameof(results)); return results; } private static ReadOnlyCollection GetValueWithReqCollection(IReadOnlyList workItems) { List results = new(); string[] segments; foreach (Value value in workItems) { segments = value.Fields.SystemTitle.Split('-'); if (segments.Length < 2) continue; if (!int.TryParse(segments[0], out int req) || req == 0) continue; results.Add(new(value, req)); } return new(results); } private static ReadOnlyCollection RemoveFrom(Dictionary keyToFIBacklogMesa, ReadOnlyCollection valueWithReqCollection) { List results = new(); foreach (ValueWithReq valueWithReq in valueWithReqCollection) { if (keyToFIBacklogMesa.Remove($"{valueWithReq.Req} - ")) continue; results.Add(valueWithReq); } return new(results); } private static void Update(WorkItemTrackingHttpClient workItemTrackingHttpClient, string sync, ValueWithReq valueWithReq) { JsonPatchDocument result = new(); AddPatch(result, "/fields/System.Tags", sync); Task workItem = workItemTrackingHttpClient.UpdateWorkItemAsync(result, valueWithReq.Value.Id); workItem.Wait(); } private static DateTime? GetCommitDate(FIBacklogMesa fiBacklogMesa) { DateTime? result; if (!DateTime.TryParseExact(fiBacklogMesa.CommitDate.Split(' ').First(), "MM/dd/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime) && dateTime != DateTime.MinValue) result = null; else result = dateTime.AddHours(12).ToUniversalTime(); return result; } private static int GetPriority(FIBacklogMesa fiBacklogMesa) { int result; if (!int.TryParse(fiBacklogMesa.Priority.Substring(0, 1), out int priority) || priority == 0 || priority > 3) result = 4; else result = priority; return result; } private static string GetTitle(FIBacklogMesa fiBacklogMesa) { string result = $"{fiBacklogMesa.Req} - {fiBacklogMesa.Subject.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)[0]}"; if (result.Length > 128) result = result.Substring(0, 127); return result; } private static string GetTitle(FIBacklogMesa fiBacklogMesa, ValueWithReq valueWithReq) { string result = $"{valueWithReq.Req} - {fiBacklogMesa.Subject.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)[0]}"; if (result.Length > 128) result = result.Substring(0, 127); return result; } private static string? GetMappedState(FIBacklogMesa fiBacklogMesa) => fiBacklogMesa.Status == "CMP" ? "Closed" : fiBacklogMesa.Status == "UAT" ? "Resolved" : fiBacklogMesa.Status == "In process" ? "Active" : null; private static void SetSyncTag(WorkItemTrackingHttpClient workItemTrackingHttpClient, ReadOnlyDictionary requestorNameToUser, Dictionary keyToFIBacklogMesa, ReadOnlyCollection valueWithReqCollection) { string key; string title; int priority; bool isBugFix; string? state; List tags; TimeSpan timeSpan; DateTime? dateTime; List compareTags; const string sync = "Sync"; FIBacklogMesa? fiBacklogMesa; foreach (ValueWithReq valueWithReq in valueWithReqCollection) { key = $"{valueWithReq.Req} - "; compareTags = GetTags(valueWithReq.Value.Fields); if (compareTags.Contains(sync)) continue; if (!keyToFIBacklogMesa.TryGetValue(key, out fiBacklogMesa)) continue; tags = GetTags(fiBacklogMesa); title = GetTitle(fiBacklogMesa, valueWithReq); isBugFix = fiBacklogMesa.Priority == "0 - BugFix"; _ = requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser); if (!string.IsNullOrEmpty(requestorUser) && (valueWithReq.Value.Fields.SystemAssignedTo is null || !valueWithReq.Value.Fields.SystemAssignedTo.UniqueName.Equals(requestorUser, StringComparison.CurrentCultureIgnoreCase))) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } if (valueWithReq.Value.Fields.SystemTitle != title) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } if (!string.IsNullOrEmpty(fiBacklogMesa.Definition) && valueWithReq.Value.Fields.SystemDescription != fiBacklogMesa.Definition.Replace("&", "&").Replace("\"", """)) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } foreach (string tag in tags) { if (compareTags.Contains(tag)) continue; _ = tags.Remove(tag); break; } if (tags.Count != compareTags.Count) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } if ((isBugFix && valueWithReq.Value.Fields.SystemWorkItemType != "Bug") || (!isBugFix && valueWithReq.Value.Fields.SystemWorkItemType == "Bug")) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } if (!isBugFix) { priority = GetPriority(fiBacklogMesa); if (valueWithReq.Value.Fields.MicrosoftVSTSCommonPriority != priority) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } } state = GetMappedState(fiBacklogMesa); if (!string.IsNullOrEmpty(state) && valueWithReq.Value.Fields.SystemState != state) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } if (!isBugFix && int.TryParse(fiBacklogMesa.EstEffortDays, out int estEffortDays) && valueWithReq.Value.Fields.Effort != estEffortDays) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } dateTime = GetCommitDate(fiBacklogMesa); if (dateTime is not null) { timeSpan = new(valueWithReq.Value.Fields.TargetDate.Ticks - dateTime.Value.Ticks); if (timeSpan.Hours is > 32 or < -32) { Update(workItemTrackingHttpClient, sync, valueWithReq); continue; } } } } private static ReadOnlyDictionary> GetForceUpdatedByCollection(ReadOnlyDictionary assignedToNameToEncodedPAT, ReadOnlyDictionary assignedToNameToUser, Dictionary keyToFIBacklogMesa, ReadOnlyCollection valueWithReqCollection) { Dictionary> results = new(); string key; List? reqCollection; FIBacklogMesa? fiBacklogMesa; foreach (ValueWithReq valueWithReq in valueWithReqCollection) { key = $"{valueWithReq.Req} - "; if (!keyToFIBacklogMesa.TryGetValue(key, out fiBacklogMesa)) continue; if (string.IsNullOrEmpty(fiBacklogMesa.AssignedTo)) continue; if (!assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser)) continue; if (!valueWithReq.Value.Fields.SystemChangedBy.UniqueName.Equals(assignedToUser, StringComparison.CurrentCultureIgnoreCase)) { if (!assignedToNameToEncodedPAT.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToEncodedPAT) || assignedToEncodedPAT.Length < 15) continue; if (!results.TryGetValue(assignedToEncodedPAT, out reqCollection)) { results.Add(assignedToEncodedPAT, new()); if (!results.TryGetValue(assignedToEncodedPAT, out reqCollection)) throw new NotSupportedException(); } reqCollection.Add(valueWithReq.Value.Id); } } return new(results); } private static ReadOnlyDictionary> GetForceUpdatedByCollectionDelete(string forceUpdatedByComment, ReadOnlyDictionary assignedToNameToEncodedPAT, ReadOnlyDictionary assignedToNameToUser, Dictionary keyToFIBacklogMesa, ReadOnlyCollection valueWithReqCollection) { Dictionary> results = new(); string key; List? reqCollection; FIBacklogMesa? fiBacklogMesa; foreach (ValueWithReq valueWithReq in valueWithReqCollection) { key = $"{valueWithReq.Req} - "; if (!keyToFIBacklogMesa.TryGetValue(key, out fiBacklogMesa)) continue; if (string.IsNullOrEmpty(fiBacklogMesa.AssignedTo)) continue; if (!assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser)) continue; if (valueWithReq.Value.Fields.SystemHistory == forceUpdatedByComment) { if (!assignedToNameToEncodedPAT.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToEncodedPAT) || assignedToEncodedPAT.Length < 15) continue; if (!results.TryGetValue(assignedToEncodedPAT, out reqCollection)) { results.Add(assignedToEncodedPAT, new()); if (!results.TryGetValue(assignedToEncodedPAT, out reqCollection)) throw new NotSupportedException(); } reqCollection.Add(new int[] { valueWithReq.Value.Id, valueWithReq.Value.CommentVersionRef.CommentId }); } } return new(results); } private static void Post(HttpClient httpClient, string basePage, string api, string query, HttpContent httpContent) { Task httpResponseMessageTask = httpClient.PostAsync(string.Concat(basePage, api, query), httpContent); httpResponseMessageTask.Wait(); if (!httpResponseMessageTask.Result.IsSuccessStatusCode) throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString()); Task stringTask = httpResponseMessageTask.Result.Content.ReadAsStringAsync(); stringTask.Wait(); } private static void ForceUpdatedBy(HttpClient httpClient, string basePage, string project, string api, string forceUpdatedByComment, ReadOnlyDictionary> updateCollection) { string stringPayload; HttpContent httpContent; HttpClient keyHttpClient; AuthenticationHeaderValue authenticationHeaderValue; foreach (KeyValuePair> keyValuePair in updateCollection) { keyHttpClient = new() { BaseAddress = httpClient.BaseAddress }; authenticationHeaderValue = keyHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", keyValuePair.Key); if (authenticationHeaderValue.Parameter != keyValuePair.Key) throw new NotSupportedException(); foreach (int id in keyValuePair.Value) { stringPayload = JsonSerializer.Serialize(new { text = forceUpdatedByComment }); httpContent = new StringContent($"{stringPayload}", Encoding.UTF8, "application/json"); Post(keyHttpClient, $"{basePage}/{project}", api, $"/workitems/{id}/comments?api-version=5.1-preview.3", httpContent); } } } private static void Delete(HttpClient httpClient, string basePage, string api, string query) { Task httpResponseMessageTask = httpClient.DeleteAsync(string.Concat(basePage, api, query)); httpResponseMessageTask.Wait(); if (!httpResponseMessageTask.Result.IsSuccessStatusCode) throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString()); Task stringTask = httpResponseMessageTask.Result.Content.ReadAsStringAsync(); stringTask.Wait(); } private static void ForceUpdatedByDelete(HttpClient httpClient, string basePage, string project, string api, ReadOnlyDictionary> deleteCollection) { HttpClient keyHttpClient; AuthenticationHeaderValue authenticationHeaderValue; foreach (KeyValuePair> keyValuePair in deleteCollection) { keyHttpClient = new() { BaseAddress = httpClient.BaseAddress }; authenticationHeaderValue = keyHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", keyValuePair.Key); if (authenticationHeaderValue.Parameter != keyValuePair.Key) throw new NotSupportedException(); foreach (int[] idCollection in keyValuePair.Value) { if (idCollection.Length != 2) throw new NotSupportedException(); Delete(keyHttpClient, $"{basePage}/{project}", api, $"/workitems/{idCollection[0]}/comments/{idCollection[1]}?api-version=7.0-preview.3"); } } } private static string GetDescription(FIBacklogMesa fiBacklogMesa, DateTime dateTime) => $"Req:{fiBacklogMesa.Req}; Submitted:{(dateTime == DateTime.MinValue ? fiBacklogMesa.Submitted : dateTime.ToString("d-MMM-yy"))}; Requestor:{fiBacklogMesa.Requestor}; AssignedTo:{fiBacklogMesa.AssignedTo}; SecondResource:{fiBacklogMesa.SecondResource}; Systems:{fiBacklogMesa.SystemS}; "; private static void Update(HttpClient httpClient, string basePage, string api, string query, HttpContent httpContent) { #if Windows Task httpResponseMessageTask = httpClient.PatchAsync(string.Concat(basePage, api, query), httpContent); httpResponseMessageTask.Wait(); if (!httpResponseMessageTask.Result.IsSuccessStatusCode) throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString()); Task stringTask = httpResponseMessageTask.Result.Content.ReadAsStringAsync(); stringTask.Wait(); #endif } private static void UpdateIds(HttpClient httpClient, string basePage, string api, string[] checkFiles) { int? idIndex; string[] lines; string[] segments; string stringPayload; HttpContent httpContent; foreach (string checkFile in checkFiles) { idIndex = null; lines = File.ReadAllLines(checkFile); if (lines.Length < 1) continue; segments = lines.First().Split(','); for (int i = 0; i < segments.Length; i++) { if (segments[i] == "ID") { idIndex = i; break; } } if (idIndex is null) continue; for (int i = 1; i < lines.Length; i++) { segments = lines[i].Split(','); if (segments.Length < idIndex.Value) continue; var payload = new { op = "replace", path = "/fields/System.IterationPath", value = "Mesa_FI" }; stringPayload = JsonSerializer.Serialize(payload); httpContent = new StringContent($"[{stringPayload}]", Encoding.UTF8, "application/json-patch+json"); Update(httpClient, basePage, api, $"/workitems/{segments[idIndex.Value].Replace("\"", string.Empty)}?api-version=1.0", httpContent); } } } private static JsonPatchDocument GetUATDocument(string project, ReadOnlyDictionary requestorNameToUser, DateTime dateTime, FIBacklogMesa fiBacklogMesa) { JsonPatchDocument result = new(); AddPatch(result, "/fields/System.AreaPath", project); string description = GetDescription(fiBacklogMesa, dateTime); AddPatch(result, "/fields/System.IterationPath", project); AddPatch(result, "/fields/System.Title", $"{fiBacklogMesa.Req} - UAT"); AddPatch(result, "/fields/System.Description", description); if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser)) AddPatch(result, "/fields/System.AssignedTo", requestorUser); return result; } private static JsonPatchDocument GetBugDocument(string project, ReadOnlyDictionary assignedToNameToUser, Task uatWorkItemTask, FIBacklogMesa fiBacklogMesa) { JsonPatchDocument result = new(); if (uatWorkItemTask.Result.Id is null) throw new NotSupportedException(); string title = GetTitle(fiBacklogMesa); AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = uatWorkItemTask.Result.Url }); AddPatch(result, "/fields/System.AreaPath", project); AddPatch(result, "/fields/System.IterationPath", project); AddPatch(result, "/fields/System.Title", title); string? state = GetMappedState(fiBacklogMesa); if (!string.IsNullOrEmpty(state)) AddPatch(result, "/fields/System.State", state); if (!string.IsNullOrEmpty(fiBacklogMesa.Definition)) AddPatch(result, "/fields/System.Description", fiBacklogMesa.Definition); if (assignedToNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser)) AddPatch(result, "/fields/System.AssignedTo", requestorUser); return result; } private static JsonPatchDocument GetDeveloperTaskDocument(string project, ReadOnlyDictionary assignedToNameToUser, FIBacklogMesa fiBacklogMesa) { JsonPatchDocument result = new(); AddPatch(result, "/fields/System.AreaPath", project); AddPatch(result, "/fields/System.IterationPath", project); AddPatch(result, "/fields/System.Title", $"{fiBacklogMesa.Req} - Developer Task"); if (assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser)) AddPatch(result, "/fields/System.AssignedTo", assignedToUser); return result; } private static JsonPatchDocument GetSecondDeveloperTaskDocument(string project, ReadOnlyDictionary assignedToNameToUser, FIBacklogMesa fiBacklogMesa) { JsonPatchDocument result = new(); AddPatch(result, "/fields/System.AreaPath", project); AddPatch(result, "/fields/System.IterationPath", project); AddPatch(result, "/fields/System.Title", $"{fiBacklogMesa.Req} - Developer Task"); if (assignedToNameToUser.TryGetValue(fiBacklogMesa.SecondResource, out string? secondResourceUser)) AddPatch(result, "/fields/System.AssignedTo", secondResourceUser); return result; } private static JsonPatchDocument GetUserStoryDocument(string project, ReadOnlyDictionary requestorNameToUser, Task uatWorkItemTask, FIBacklogMesa fiBacklogMesa) { JsonPatchDocument result = new(); if (uatWorkItemTask?.Result.Id is null) throw new NotSupportedException(); AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = uatWorkItemTask.Result.Url }); AddPatch(result, "/fields/System.AreaPath", project); AddPatch(result, "/fields/System.IterationPath", project); AddPatch(result, "/fields/System.Title", $"{fiBacklogMesa.Req} - User Story"); string? state = GetMappedState(fiBacklogMesa); if (!string.IsNullOrEmpty(state)) AddPatch(result, "/fields/System.State", state); if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser)) AddPatch(result, "/fields/System.AssignedTo", requestorUser); return result; } private static JsonPatchDocument GetAssignedToRelationDocument(Task assignedToWorkItemTask) { JsonPatchDocument result = new(); if (assignedToWorkItemTask.Result.Id is null) throw new NotSupportedException(); AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = assignedToWorkItemTask.Result.Url }); return result; } private static JsonPatchDocument GetSecondResourceDocument(Task secondResourceWorkItemTask) { JsonPatchDocument result = new(); if (secondResourceWorkItemTask?.Result.Id is null) throw new NotSupportedException(); AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = secondResourceWorkItemTask.Result.Url }); return result; } private static JsonPatchDocument GetFeatureDocument(string project, ReadOnlyDictionary requestorNameToUser, List tags, Task? userStoryWorkItemTask, FIBacklogMesa fiBacklogMesa) { JsonPatchDocument result = new(); if (userStoryWorkItemTask?.Result.Id is null) throw new NotSupportedException(); string title = GetTitle(fiBacklogMesa); int priority = GetPriority(fiBacklogMesa); AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = userStoryWorkItemTask.Result.Url }); AddPatch(result, "/fields/System.AreaPath", project); if (tags.Count > 0) { AddPatch(result, "/fields/System.Tags", tags.Last()); tags.RemoveAt(tags.Count - 1); } AddPatch(result, "/fields/System.IterationPath", project); AddPatch(result, "/fields/Microsoft.VSTS.Common.Priority", priority); if (!string.IsNullOrEmpty(fiBacklogMesa.Definition)) AddPatch(result, "/fields/System.Description", fiBacklogMesa.Definition); string? state = GetMappedState(fiBacklogMesa); if (!string.IsNullOrEmpty(state)) AddPatch(result, "/fields/System.State", state); if (!string.IsNullOrEmpty(fiBacklogMesa.EstEffortDays) && int.TryParse(fiBacklogMesa.EstEffortDays, out int estEffortDays) && estEffortDays != 0) AddPatch(result, "/fields/Microsoft.VSTS.Scheduling.Effort", estEffortDays); DateTime? dateTime = GetCommitDate(fiBacklogMesa); if (dateTime is not null) AddPatch(result, "/fields/Microsoft.VSTS.Scheduling.TargetDate", dateTime); if (!string.IsNullOrEmpty(fiBacklogMesa.Updates)) AddPatch(result, "/fields/System.History", fiBacklogMesa.Updates); AddPatch(result, "/fields/System.Title", title); if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser)) AddPatch(result, "/fields/System.AssignedTo", requestorUser); // https://tfs.intra.infineon.com/tfs/ManufacturingIT/Mesa_FI/_apis/wit/workitemtypes/feature/fields?api-version=7.0 return result; } private static List GetTags(FIBacklogMesa fiBacklogMesa) { List results = new(); foreach (string tag in fiBacklogMesa.SystemS.Split('/')) { if (string.IsNullOrEmpty(tag.Trim())) continue; results.Add(tag.Trim()); } return results; } private static List GetTags(Fields fields) { List results = new(); if (!string.IsNullOrEmpty(fields.SystemTags)) { foreach (string tag in fields.SystemTags.Split(';')) { if (string.IsNullOrEmpty(tag.Trim())) continue; results.Add(tag.Trim()); } } return results; } private static void DoWork(WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, ReadOnlyDictionary assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, FIBacklogMesa fiBacklogMesa) { DateTime dateTime; JsonPatchDocument tagDocument; List tags = GetTags(fiBacklogMesa); Task? secondResourceWorkItemTask = null; bool isBugFix = fiBacklogMesa.Priority == "0 - BugFix"; if (!DateTime.TryParse(fiBacklogMesa.Submitted, out dateTime)) dateTime = DateTime.MinValue; JsonPatchDocument uatDocument = GetUATDocument(project, requestorNameToUser, dateTime, fiBacklogMesa); Task uatWorkItemTask = workItemTrackingHttpClient.CreateWorkItemAsync(uatDocument, project, "Task"); uatWorkItemTask.Wait(); if (isBugFix) { JsonPatchDocument bugDocument = GetBugDocument(project, assignedToNameToUser, uatWorkItemTask, fiBacklogMesa); Task bugWorkItemTask = workItemTrackingHttpClient.CreateWorkItemAsync(bugDocument, project, "Bug"); bugWorkItemTask.Wait(); } if (!isBugFix) { JsonPatchDocument developerTaskDocument = GetDeveloperTaskDocument(project, assignedToNameToUser, fiBacklogMesa); Task assignedToWorkItemTask = workItemTrackingHttpClient.CreateWorkItemAsync(developerTaskDocument, project, "Task"); assignedToWorkItemTask.Wait(); if (!string.IsNullOrEmpty(fiBacklogMesa.SecondResource)) { JsonPatchDocument secondDeveloperTaskDocument = GetSecondDeveloperTaskDocument(project, assignedToNameToUser, fiBacklogMesa); secondResourceWorkItemTask = workItemTrackingHttpClient.CreateWorkItemAsync(secondDeveloperTaskDocument, project, "Task"); secondResourceWorkItemTask.Wait(); } JsonPatchDocument userStoryDocument = GetUserStoryDocument(project, requestorNameToUser, uatWorkItemTask, fiBacklogMesa); Task userStoryWorkItemTask = workItemTrackingHttpClient.CreateWorkItemAsync(userStoryDocument, project, "User Story"); userStoryWorkItemTask.Wait(); if (userStoryWorkItemTask?.Result.Id is null) throw new NotSupportedException(); JsonPatchDocument assignedToRelationDocument = GetAssignedToRelationDocument(assignedToWorkItemTask); userStoryWorkItemTask = workItemTrackingHttpClient.UpdateWorkItemAsync(assignedToRelationDocument, userStoryWorkItemTask.Result.Id.Value); userStoryWorkItemTask.Wait(); if (secondResourceWorkItemTask is not null) { if (userStoryWorkItemTask.Result.Id is null) throw new NotSupportedException(); JsonPatchDocument secondResourceDocument = GetSecondResourceDocument(secondResourceWorkItemTask); userStoryWorkItemTask = workItemTrackingHttpClient.UpdateWorkItemAsync(secondResourceDocument, userStoryWorkItemTask.Result.Id.Value); userStoryWorkItemTask.Wait(); } JsonPatchDocument featureDocument = GetFeatureDocument(project, requestorNameToUser, tags, userStoryWorkItemTask, fiBacklogMesa); Task featureWorkItemTask = workItemTrackingHttpClient.CreateWorkItemAsync(featureDocument, project, "Feature"); featureWorkItemTask.Wait(); for (int i = tags.Count - 1; i > -1; i--) { if (featureWorkItemTask.Result.Id is null) throw new NotSupportedException(); tagDocument = new(); AddPatch(tagDocument, "/fields/System.Tags", tags[i]); tags.RemoveAt(i); featureWorkItemTask = workItemTrackingHttpClient.UpdateWorkItemAsync(tagDocument, featureWorkItemTask.Result.Id.Value); featureWorkItemTask.Wait(); } } } private void Parse(IFileRead fileRead, Logistics logistics, List fileInfoCollection, HttpClient httpClient, string basePage, string api, string query, WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, ReadOnlyDictionary assignedToNameToEncodedPAT, ReadOnlyDictionary assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, string json, bool forceUpdatedBy, bool forceDeleteUpdatedBy) { if (fileRead is null) throw new NullReferenceException(); if (logistics is null) throw new NullReferenceException(); if (fileInfoCollection is null) throw new NullReferenceException(); int counter = 0; string forceUpdatedByComment = "Force updated by"; string? directory = Path.GetDirectoryName(fileRead.ReportFullPath) ?? throw new Exception(); string[] checkFiles = Directory.GetFiles(directory, "*.csv", SearchOption.TopDirectoryOnly); if (checkFiles.Length != 0) UpdateIds(httpClient, basePage, api, checkFiles); else { string ids = GetIds(httpClient, basePage, api, query); Dictionary keyToFIBacklogMesa = GetFIBacklogMesaCollection(json); Value[] workItems = string.IsNullOrEmpty(ids) ? Array.Empty() : GetWorkItems(fileRead, httpClient, basePage, api, ids); int count = keyToFIBacklogMesa.Count; ReadOnlyCollection valueWithReqCollection = GetValueWithReqCollection(workItems); SetSyncTag(workItemTrackingHttpClient, requestorNameToUser, keyToFIBacklogMesa, valueWithReqCollection); ReadOnlyDictionary> updateCollection = GetForceUpdatedByCollection(assignedToNameToEncodedPAT, assignedToNameToUser, keyToFIBacklogMesa, valueWithReqCollection); if (updateCollection.Count > 0 && forceUpdatedBy) ForceUpdatedBy(httpClient, basePage, project, api, forceUpdatedByComment, updateCollection); if (forceDeleteUpdatedBy) { ReadOnlyDictionary> deleteCollection = GetForceUpdatedByCollectionDelete(forceUpdatedByComment, assignedToNameToEncodedPAT, assignedToNameToUser, keyToFIBacklogMesa, valueWithReqCollection); if (deleteCollection.Count > 0 && forceDeleteUpdatedBy) ForceUpdatedByDelete(httpClient, basePage, project, api, deleteCollection); } ReadOnlyCollection extra = RemoveFrom(keyToFIBacklogMesa, valueWithReqCollection); if (count != extra.Count) { } if (count != keyToFIBacklogMesa.Count) { } _Details.AddRange(workItems); foreach (KeyValuePair keyValuePair in keyToFIBacklogMesa) { if (counter > 5) break; if (!fileRead.IsEAFHosted) continue; DoWork(workItemTrackingHttpClient, project, assignedToNameToUser, requestorNameToUser, keyValuePair.Value); counter++; } } } }