file-folder-helper/ADO2024/PI3/Helper-2024-08-09.cs
Mike Phares f0ebc5b574 HgCV
CDE
Use of LinkTarget
2024-11-01 17:08:04 -07:00

719 lines
36 KiB
C#

#if WorkItems
using File_Folder_Helper.Day.Q32024.ConvertExcelToJson;
using File_Folder_Helper.Day.Q32024.WorkItems;
using File_Folder_Helper.Models;
#endif
using Microsoft.Extensions.Logging;
#if WorkItems
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Web;
#endif
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240809
{
#if WorkItems
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<string, FIBacklogMesa> GetFIBacklogMesaCollection(string json)
{
Dictionary<string, FIBacklogMesa> results = [];
string key;
FIBacklogMesa[]? fiBacklogMesaCollection;
fiBacklogMesaCollection = JsonSerializer.Deserialize(json, FIBacklogMesaCollectionSourceGenerationContext.Default.FIBacklogMesaArray);
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<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, query));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
WIQL.Root? root = JsonSerializer.Deserialize(streamTask.Result, WIQL.WIQLRootSourceGenerationContext.Default.Root);
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 ReadOnlyCollection<ValueWithReq> GetWorkItems(HttpClient httpClient, string basePage, string api, string sourceDirectory, string ids)
{
List<ValueWithReq> results = [];
int req;
string json;
string file;
Value? value;
string[] segments;
JsonElement[] jsonElements;
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems?ids={ids}"));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
JsonProperty[] jsonProperties = result.Value.EnumerateObject().ToArray();
foreach (JsonProperty jsonProperty in jsonProperties)
{
if (jsonProperty.Value.ValueKind != JsonValueKind.Array)
continue;
jsonElements = jsonProperty.Value.EnumerateArray().ToArray();
foreach (JsonElement jsonElement in jsonElements)
{
json = jsonElement.GetRawText();
value = JsonSerializer.Deserialize(json, ValueSourceGenerationContext.Default.Value);
if (value is null)
continue;
segments = value.Fields.SystemTitle.Split('-');
if (segments.Length < 2)
continue;
if (!int.TryParse(segments[0], out req) || req == 0)
continue;
file = Path.Combine(sourceDirectory, $"{req}-{value.Id}.json");
File.WriteAllText(file, json);
results.Add(new(value, req, json));
}
}
return new(results);
}
private static ReadOnlyCollection<ValueWithReq> RemoveFrom(Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa, ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
List<ValueWithReq> results = [];
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
if (keyToFIBacklogMesa.Remove($"{valueWithReq.Req} - "))
continue;
results.Add(valueWithReq);
}
return new(results);
}
private static ReadOnlyCollection<Models.Comment> GetComments(HttpClient httpClient, string basePage, string api, string sourceDirectory, int req, int id)
{
List<Models.Comment> results = [];
string json;
string file;
Models.Comment? comment;
JsonElement[] jsonElements;
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems/{id}/comments?api-version=7.0-preview.3"));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
JsonProperty[] jsonProperties = result.Value.EnumerateObject().ToArray();
foreach (JsonProperty jsonProperty in jsonProperties)
{
if (jsonProperty.Value.ValueKind != JsonValueKind.Array)
continue;
jsonElements = jsonProperty.Value.EnumerateArray().ToArray();
foreach (JsonElement jsonElement in jsonElements)
{
json = jsonElement.GetRawText();
comment = JsonSerializer.Deserialize(jsonElement, CommentSourceGenerationContext.Default.Comment);
if (comment is null || comment.WorkItemId is null || comment.Id is null)
continue;
file = Path.Combine(sourceDirectory, $"{req}-{id}-{comment.Id}-comments.json");
File.WriteAllText(file, json);
results.Add(comment);
}
}
return new(results);
}
private static void UpdateComment(HttpClient httpClient,
string basePage,
string api,
int id,
FIBacklogMesa fiBacklogMesa,
Models.Comment comment)
{
DateTime submittedDateTime;
if (!DateTime.TryParse(fiBacklogMesa.Submitted, out submittedDateTime))
submittedDateTime = DateTime.MinValue;
string updatesWithSubmitted = $"{fiBacklogMesa.Updates}<br>&nbsp;<br>{submittedDateTime:MM/dd/yyyy} - Submitted by {fiBacklogMesa.Requestor}";
string json = JsonSerializer.Serialize(new { text = updatesWithSubmitted });
StringContent stringContent = new(json, Encoding.UTF8, "application/json");
string requestUri = string.Concat(basePage, api, $"/workitems/{id}/comments/{comment.Id}?api-version=7.0-preview.3");
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.PatchAsync(requestUri, stringContent);
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
}
private static void UpdateAllWorkItemsPharesComment(HttpClient httpClient,
string basePage,
string api,
string sourceDirectory,
Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
string key;
FIBacklogMesa? fIBacklogMesa;
ReadOnlyCollection<Models.Comment> comments;
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
if (valueWithReq.Value.Fields.SystemCommentCount == 0)
continue;
key = $"{valueWithReq.Req} - ";
if (!keyToFIBacklogMesa.TryGetValue(key, out fIBacklogMesa))
continue;
comments = GetComments(httpClient, basePage, api, sourceDirectory, valueWithReq.Req, valueWithReq.Value.Id);
foreach (Models.Comment comment in comments)
{
if (comment.CreatedBy?.UniqueName is null || !comment.CreatedBy.UniqueName.Contains("Phares", StringComparison.CurrentCultureIgnoreCase))
continue;
UpdateComment(httpClient, basePage, api, valueWithReq.Value.Id, fIBacklogMesa, comment);
}
}
}
private static void UpdateIteration(HttpClient httpClient,
string basePage,
string api,
int id,
int rev)
{
string json = /*lang=json,strict*/ string.Concat("[ { \"op\": \"test\", \"path\": \"/rev\", \"value\": ", rev, " }, { \"op\": \"replace\", \"path\": \"/fields/System.IterationPath\", \"value\": \"ART SPS\" } ]");
StringContent stringContent = new(json, Encoding.UTF8, "application/json-patch+json");
string requestUri = string.Concat(basePage, api, $"/workitems/{id}?api-version=1.0");
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.PatchAsync(requestUri, stringContent);
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
}
private static void UpdateAllFeaturesNotArtSPS(HttpClient httpClient,
string basePage,
string api,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
if (valueWithReq.Value.Fields.SystemIterationPath != "ART SPS\\2024")
continue;
UpdateIteration(httpClient, basePage, api, valueWithReq.Value.Id, valueWithReq.Value.Rev);
}
}
private static DateTime? GetCommitDate(FIBacklogMesa fiBacklogMesa)
{
DateTime? result;
DateTime dateTime;
DateTime minDateTime = DateTime.MinValue.AddYears(10);
string commitDate = fiBacklogMesa.CommitDate.Split(' ')[0];
if (string.IsNullOrEmpty(commitDate))
result = null;
else
{
if (DateTime.TryParseExact(commitDate, "MM/dd/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime) && dateTime >= minDateTime)
result = dateTime.AddHours(12).ToUniversalTime();
else
{
if (DateTime.TryParseExact(commitDate, "dd-MMM-yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime) && dateTime >= minDateTime)
result = dateTime.AddHours(12).ToUniversalTime();
else
{
if (DateTime.TryParse(commitDate, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime) && dateTime >= minDateTime)
result = dateTime.AddHours(12).ToUniversalTime();
else
result = null;
}
}
}
return result;
}
private static int GetPriority(FIBacklogMesa fiBacklogMesa)
{
int result;
if (string.IsNullOrEmpty(fiBacklogMesa.Priority) || !int.TryParse(fiBacklogMesa.Priority[..1], out int priority) || priority == 0 || priority > 3)
result = 4;
else
result = priority;
return result;
}
private static int? GetPrioritySubset(FIBacklogMesa fiBacklogMesa)
{
int? result;
if (string.IsNullOrEmpty(fiBacklogMesa.PrioritySubset) || !int.TryParse(fiBacklogMesa.PrioritySubset[..1], out int prioritySubset) || prioritySubset == 0 || prioritySubset > 3)
result = null;
else
result = prioritySubset;
return result;
}
private static string GetIterationPath(string project, DateTime submittedDateTime) =>
submittedDateTime.Year != 2024 ? project : string.Concat(project, "\\", submittedDateTime.Year);
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[..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[..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 JsonPatchDocument GetBugDocument(string project, string site, ReadOnlyDictionary<string, string> assignedToNameToUser, ReadOnlyDictionary<string, string> requestorNameToUser, Task<WorkItem>? uatWorkItemTask, FIBacklogMesa fiBacklogMesa, DateTime submittedDateTime)
{
JsonPatchDocument result = [];
string title = GetTitle(fiBacklogMesa);
string iterationPath = GetIterationPath(project, submittedDateTime);
if (uatWorkItemTask?.Result.Id is not null)
AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = uatWorkItemTask.Result.Url });
AddPatch(result, "/fields/System.AreaPath", string.Concat(project, "\\", site));
AddPatch(result, "/fields/System.IterationPath", iterationPath);
AddPatch(result, "/fields/System.Title", title);
AddPatch(result, "/fields/System.CreatedDate", submittedDateTime.AddHours(12).ToUniversalTime());
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.Subject}<br>&nbsp;<br>{fiBacklogMesa.Definition}");
if (assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser))
AddPatch(result, "/fields/System.AssignedTo", assignedToUser);
if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser))
AddPatch(result, "/fields/Custom.Requester", requestorUser);
return result;
}
private static JsonPatchDocument GetFeatureDocument(string project, string site, ReadOnlyDictionary<string, string> requestorNameToUser, ReadOnlyDictionary<string, string> assignedToNameToUser, List<string> tags, Task<WorkItem>? userStoryWorkItemTask, FIBacklogMesa fiBacklogMesa, DateTime submittedDateTime)
{
JsonPatchDocument result = [];
string title = GetTitle(fiBacklogMesa);
int priority = GetPriority(fiBacklogMesa);
string? state = GetMappedState(fiBacklogMesa);
int? prioritySubset = GetPrioritySubset(fiBacklogMesa);
if (prioritySubset is not null)
AddPatch(result, "/fields/Microsoft.VSTS.Common.TimeCriticality", prioritySubset);
string iterationPath = GetIterationPath(project, submittedDateTime);
if (userStoryWorkItemTask?.Result.Id is not null)
AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = userStoryWorkItemTask.Result.Url });
AddPatch(result, "/fields/System.AreaPath", string.Concat(project, "\\", site));
if (tags.Count > 0)
{
AddPatch(result, "/fields/System.Tags", tags.Last());
tags.RemoveAt(tags.Count - 1);
}
AddPatch(result, "/fields/System.IterationPath", iterationPath);
AddPatch(result, "/fields/Microsoft.VSTS.Common.Priority", priority);
if (!string.IsNullOrEmpty(fiBacklogMesa.Definition))
AddPatch(result, "/fields/System.Description", $"{fiBacklogMesa.Subject}<br>&nbsp;<br>{fiBacklogMesa.Definition}");
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);
AddPatch(result, "/fields/System.CreatedDate", submittedDateTime.AddHours(12).ToUniversalTime());
if (assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser))
AddPatch(result, "/fields/System.AssignedTo", assignedToUser);
if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser))
AddPatch(result, "/fields/Custom.Requester", requestorUser);
// https://tfs.intra.infineon.com/tfs/ManufacturingIT/Mesa_FI/_apis/wit/workitemtypes/feature/fields?api-version=7.0
return result;
}
private static List<string> GetTags(FIBacklogMesa fiBacklogMesa)
{
List<string> results = [];
foreach (string tag in fiBacklogMesa.SystemS.Split('/'))
{
if (string.IsNullOrEmpty(tag.Trim()))
continue;
results.Add(tag.Trim());
}
return results;
}
private static List<string> GetTags(Fields fields)
{
List<string> results = [];
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 CreateWorkItem(WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, string site, ReadOnlyDictionary<string, string> assignedToNameToUser, ReadOnlyDictionary<string, string> requestorNameToUser, FIBacklogMesa fiBacklogMesa)
{
DateTime submittedDateTime;
JsonPatchDocument tagDocument;
Task<WorkItem>? workItem = null;
List<string> tags = GetTags(fiBacklogMesa);
bool isBugFix = fiBacklogMesa.Priority == "0 - BugFix";
if (assignedToNameToUser.Count > requestorNameToUser.Count)
throw new Exception();
if (!DateTime.TryParse(fiBacklogMesa.Submitted, out submittedDateTime))
submittedDateTime = DateTime.MinValue;
if (isBugFix)
{
Task<WorkItem>? uatWorkItemTask = null;
JsonPatchDocument bugDocument = GetBugDocument(project, site, requestorNameToUser, assignedToNameToUser, uatWorkItemTask, fiBacklogMesa, submittedDateTime);
workItem = workItemTrackingHttpClient.CreateWorkItemAsync(bugDocument, project, "Bug");
workItem.Wait();
}
if (!isBugFix)
{
Task<WorkItem>? userStoryWorkItemTask = null;
JsonPatchDocument featureDocument = GetFeatureDocument(project, site, requestorNameToUser, assignedToNameToUser, tags, userStoryWorkItemTask, fiBacklogMesa, submittedDateTime);
workItem = workItemTrackingHttpClient.CreateWorkItemAsync(featureDocument, project, "Feature");
workItem.Wait();
}
for (int i = tags.Count - 1; i > -1; i--)
{
if (workItem is null)
continue;
if (workItem.Result.Id is null)
throw new NotSupportedException();
tagDocument = [];
AddPatch(tagDocument, "/fields/System.Tags", tags[i]);
tags.RemoveAt(i);
workItem = workItemTrackingHttpClient.UpdateWorkItemAsync(tagDocument, workItem.Result.Id.Value);
workItem.Wait();
}
}
private static void KillTime(int loops)
{
for (int i = 1; i < loops; i++)
Thread.Sleep(500);
}
private static void Update(HttpClient httpClient, string basePage, string api, string query, HttpContent httpContent)
{
#if Windows
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.PatchAsync(string.Concat(basePage, api, query), httpContent);
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<string> stringTask = httpResponseMessageTask.Result.Content.ReadAsStringAsync();
stringTask.Wait();
#endif
KillTime(30);
}
private static void Update(HttpClient httpClient, string basePage, string api, WorkItemTrackingHttpClient workItemTrackingHttpClient, string sync, ValueWithReq valueWithReq)
{
JsonPatchDocument result = [];
AddPatch(result, "/fields/System.Tags", sync);
Task<WorkItem> workItem = workItemTrackingHttpClient.UpdateWorkItemAsync(result, valueWithReq.Value.Id);
workItem.Wait();
if (result is null)
{
var payload = new
{
op = "replace",
path = "/fields/System.IterationPath",
value = "Mesa_FI"
};
string stringPayload = JsonSerializer.Serialize(payload);
HttpContent httpContent = new StringContent($"[{stringPayload}]", Encoding.UTF8, "application/json-patch+json");
Update(httpClient, basePage, api, $"/workitems/{valueWithReq.Value.Id}?api-version=1.0", httpContent);
}
}
private static int SetSyncTag(HttpClient httpClient,
string basePage,
string api,
WorkItemTrackingHttpClient workItemTrackingHttpClient,
ReadOnlyDictionary<string, string> assignedToNameToUser,
ReadOnlyDictionary<string, string> requestorNameToUser,
Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
int result = 0;
string key;
string title;
int priority;
bool isBugFix;
string? state;
List<string> tags;
TimeSpan timeSpan;
DateTime? dateTime;
List<string> compareTags;
const string sync = "Sync";
FIBacklogMesa? fiBacklogMesa;
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
key = $"{valueWithReq.Req} - ";
if (!string.IsNullOrEmpty(key)) // Forced to skip logic
continue;
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.CustomRequester is null || !valueWithReq.Value.Fields.CustomRequester.UniqueName.Equals(requestorUser, StringComparison.CurrentCultureIgnoreCase)))
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
_ = assignedToNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? assignedToUser);
if (!string.IsNullOrEmpty(assignedToUser) && (valueWithReq.Value.Fields.SystemAssignedTo is null || !valueWithReq.Value.Fields.SystemAssignedTo.UniqueName.Equals(assignedToUser, StringComparison.CurrentCultureIgnoreCase)))
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if (valueWithReq.Value.Fields.SystemTitle != title)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
foreach (string tag in tags)
{
if (compareTags.Contains(tag))
continue;
_ = tags.Remove(tag);
break;
}
if (tags.Count != compareTags.Count)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if ((isBugFix && valueWithReq.Value.Fields.SystemWorkItemType != "Bug") || (!isBugFix && valueWithReq.Value.Fields.SystemWorkItemType == "Bug"))
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if (!isBugFix)
{
priority = GetPriority(fiBacklogMesa);
if (valueWithReq.Value.Fields.MicrosoftVSTSCommonPriority != priority)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
}
state = GetMappedState(fiBacklogMesa);
if (!string.IsNullOrEmpty(state) && valueWithReq.Value.Fields.SystemState != state)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if (!isBugFix && int.TryParse(fiBacklogMesa.EstEffortDays, out int estEffortDays) && valueWithReq.Value.Fields.MicrosoftVSTSSchedulingEffort != estEffortDays)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
dateTime = GetCommitDate(fiBacklogMesa);
if (dateTime is not null)
{
timeSpan = new(valueWithReq.Value.Fields.MicrosoftVSTSSchedulingTargetDate.Ticks - dateTime.Value.Ticks);
if (timeSpan.Hours is > 32 or < -32)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
}
}
return result;
}
private static void CreateWorkItems(HttpClient httpClient,
string sourceDirectory,
string basePage,
string api,
string query,
WorkItemTrackingHttpClient workItemTrackingHttpClient,
string project,
string site,
ReadOnlyDictionary<string, string> assignedToNameToUser,
ReadOnlyDictionary<string, string> requestorNameToUser,
string json)
{
int counter = 0;
string ids = GetIds(httpClient, basePage, api, query);
Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa = GetFIBacklogMesaCollection(json);
ReadOnlyCollection<ValueWithReq> valueWithReqCollection = string.IsNullOrEmpty(ids) ? new([]) : GetWorkItems(httpClient, basePage, api, sourceDirectory, ids);
int updated = SetSyncTag(httpClient, basePage, api, workItemTrackingHttpClient, assignedToNameToUser, requestorNameToUser, keyToFIBacklogMesa, valueWithReqCollection);
if (updated == 0)
{
UpdateAllFeaturesNotArtSPS(httpClient, basePage, api, valueWithReqCollection);
UpdateAllWorkItemsPharesComment(httpClient, basePage, api, sourceDirectory, keyToFIBacklogMesa, valueWithReqCollection);
ReadOnlyCollection<ValueWithReq> extra = RemoveFrom(keyToFIBacklogMesa, valueWithReqCollection);
foreach (KeyValuePair<string, FIBacklogMesa> keyValuePair in keyToFIBacklogMesa)
{
if (keyToFIBacklogMesa.Count == extra.Count)
break;
if (keyValuePair.Value.Status is "CMP" or "CNCL")
continue;
CreateWorkItem(workItemTrackingHttpClient, project, site, assignedToNameToUser, requestorNameToUser, keyValuePair.Value);
counter++;
}
}
}
private static void CreateWorkItems(ILogger<Worker> logger, string sourceDirectory, string api, string site, string query, string project, string basePage, string baseAddress, byte[] bytes, string[] assignedToNames, string[] requestorNames, string reportFullPath, MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue, WorkItemTrackingHttpClient workItemTrackingHttpClient, HttpClient httpClient)
{
string base64 = Convert.ToBase64String(bytes);
string json = File.ReadAllText(reportFullPath);
httpClient.DefaultRequestHeaders.Authorization = new("Basic", base64);
httpClient.DefaultRequestHeaders.Accept.Add(mediaTypeWithQualityHeaderValue);
ReadOnlyDictionary<string, string> requestorNameToUser = GetRequestorNameToUser(requestorNames);
ReadOnlyDictionary<string, string> assignedToNameToUser = GetAssignedToNameToUser(assignedToNames);
logger.LogInformation("{baseAddress}{basePage}/{project}{api}{query}", baseAddress, basePage, HttpUtility.HtmlEncode(project), api, query);
CreateWorkItems(httpClient, sourceDirectory, basePage, api, query, workItemTrackingHttpClient, project, site, new(assignedToNameToUser), new(requestorNameToUser), json);
}
private static ReadOnlyDictionary<string, string> GetAssignedToNameToUser(string[] assignedToNames)
{
Dictionary<string, string> results = [];
string[] segments;
foreach (string assignedToName in assignedToNames)
{
segments = assignedToName.Split('|');
if (segments.Length != 2)
continue;
results.Add(segments[0], segments[1]);
}
return new(results);
}
private static ReadOnlyDictionary<string, string> GetRequestorNameToUser(string[] requestorNames)
{
Dictionary<string, string> results = [];
string[] segments;
foreach (string requestorName in requestorNames)
{
segments = requestorName.Split('|');
if (segments.Length != 2)
continue;
results.Add(segments[0], segments[1]);
}
return new(results);
}
internal static void CreateWorkItems(ILogger<Worker> logger, List<string> args)
{
string api = args[6];
string pat = args[8];
string site = args[2];
string query = args[7];
string project = args[5];
string basePage = args[4];
string baseAddress = args[3];
string sourceDirectory = args[0];
VssBasicCredential credential = new("", pat);
string[] requestorNames = args[11].Split(',');
string[] assignedToNames = args[10].Split(',');
byte[] bytes = Encoding.ASCII.GetBytes($":{pat}");
string reportFullPath = Path.GetFullPath(Path.Combine(sourceDirectory, args[9]));
VssConnection connection = new(new(string.Concat(baseAddress, basePage)), credential);
MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue = new("application/json");
WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
HttpClient httpClient = new(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new(baseAddress) };
CreateWorkItems(logger, sourceDirectory, api, site, query, project, basePage, baseAddress, bytes, assignedToNames, requestorNames, reportFullPath, mediaTypeWithQualityHeaderValue, workItemTrackingHttpClient, httpClient);
}
#else
internal static void CreateWorkItems(ILogger<Worker> logger, List<string> args)
{
logger.LogError("CreateWorkItems is not available in WorkItems {args[0]}", args[0]);
logger.LogError("CreateWorkItems is not available in WorkItems {args[1]}", args[1]);
}
#endif
}