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.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 assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, string json) { fileInfoCollection.Clear(); _Details = new List(); Parse(fileRead, logistics, fileInfoCollection, httpClient, basePage, api, query, workItemTrackingHttpClient, project, assignedToNameToUser, requestorNameToUser, json); } 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.Any()) 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(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)); results = JsonSerializer.Deserialize(jsonElement.Value.EnumerateObject().Last().Value); if (results is null || !results.Any()) throw new NullReferenceException(nameof(results)); return results; } private static Value[] GetExtra(IReadOnlyList workItems, Dictionary keyToFIBacklogMesa) { List results = new(); foreach (Value value in workItems) { if (!value.Fields.SystemTitle.Contains(" - ")) continue; if (keyToFIBacklogMesa.Remove(value.Fields.SystemTitle)) continue; results.Add(value); } return results.ToArray(); } 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 CORE 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, string key, 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", key); AddPatch(result, "/fields/System.Description", description); if (requestorNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, 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(); 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} - {fiBacklogMesa.Subject}"); 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? assignedToUser)) AddPatch(result, "/fields/System.AssignedTo", assignedToUser); 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"); 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(); 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); if (!string.IsNullOrEmpty(fiBacklogMesa.Definition)) AddPatch(result, "/fields/System.Description", fiBacklogMesa.Definition); if (int.TryParse(fiBacklogMesa.Priority.Substring(0, 1), out int priority) && priority != 0) AddPatch(result, "/fields/Microsoft.VSTS.Common.Priority", priority); if (!string.IsNullOrEmpty(fiBacklogMesa.EstEffortDays) && int.TryParse(fiBacklogMesa.EstEffortDays, out int estEffortDays) && estEffortDays != 0) AddPatch(result, "/fields/Microsoft.VSTS.Scheduling.Effort", estEffortDays); if (!string.IsNullOrEmpty(fiBacklogMesa.CommitDate) && DateTime.TryParseExact(fiBacklogMesa.CommitDate.Split(' ').First(), "MM/dd/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime commitDate) && commitDate != DateTime.MinValue) AddPatch(result, "/fields/Microsoft.VSTS.Scheduling.TargetDate", commitDate.AddHours(12).ToUniversalTime()); if (!string.IsNullOrEmpty(fiBacklogMesa.Updates)) AddPatch(result, "/fields/System.History", fiBacklogMesa.Updates); AddPatch(result, "/fields/System.Title", $"{fiBacklogMesa.Req} - {fiBacklogMesa.Subject}"); 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 void DoWork(WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, ReadOnlyDictionary assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, string key, FIBacklogMesa fiBacklogMesa) { DateTime dateTime; List tags = new(); JsonPatchDocument tagDocument; Task? secondResourceWorkItemTask = null; bool isBugFix = fiBacklogMesa.Priority == "0 - BugFix"; if (!DateTime.TryParse(fiBacklogMesa.Submitted, out dateTime)) dateTime = DateTime.MinValue; foreach (string tag in fiBacklogMesa.SystemS.Split('/')) { if (string.IsNullOrEmpty(tag.Trim())) continue; tags.Add(tag.Trim()); } tags.Reverse(); JsonPatchDocument uatDocument = GetUATDocument(project, requestorNameToUser, dateTime, key, 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 assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, string json) { 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? directory = Path.GetDirectoryName(fileRead.ReportFullPath) ?? throw new Exception(); string[] checkFiles = Directory.GetFiles(directory, "*.csv", SearchOption.TopDirectoryOnly); if (checkFiles.Any()) 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(httpClient, basePage, api, ids); int count = keyToFIBacklogMesa.Count; Value[] extra = GetExtra(workItems, keyToFIBacklogMesa); if (count != extra.Length) { } 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.Key, keyValuePair.Value); counter++; } } } }