#if WorkItems using File_Folder_Helper.ADO2024.PI3.WorkItems; #endif using Microsoft.Extensions.Logging; #if WorkItems using Microsoft.TeamFoundation.WorkItemTracking.WebApi; using Microsoft.VisualStudio.Services.Common; using Microsoft.VisualStudio.Services.WebApi; using System.Collections.ObjectModel; 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 Helper20240830 { #if WorkItems private record WorkItem(string AreaPath, string? AssignedTo, int? BusinessValue, DateTime ChangedDate, DateTime? ClosedDate, int CommentCount, DateTime CreatedDate, string Description, float? Effort, int Id, string IterationPath, int? Parent, int? Priority, object[] Relations, string? Requester, DateTime? ResolvedDate, int Revision, int? RiskReductionMinusOpportunityEnablement, DateTime? StartDate, string State, string Tags, DateTime? TargetDate, float? TimeCriticality, string Title, string WorkItemType, float? WeightedShortestJobFirst); private static void CompareWorkItems(ILogger logger, string sourceDirectory, string api, string site, string query, string project, string basePage, string baseAddress, byte[] bytes, MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue, WorkItemTrackingHttpClient workItemTrackingHttpClient, HttpClient httpClient) { string base64 = Convert.ToBase64String(bytes); httpClient.DefaultRequestHeaders.Authorization = new("Basic", base64); httpClient.DefaultRequestHeaders.Accept.Add(mediaTypeWithQualityHeaderValue); logger.LogInformation("{baseAddress}{basePage}/{project}{api}{query}", baseAddress, basePage, HttpUtility.HtmlEncode(project), api, query); CompareWorkItems(httpClient, sourceDirectory, basePage, api, query, workItemTrackingHttpClient, project, site); } private static void GetSingle(HttpClient httpClient, string basePage, string api, string targetFileLocation, int id) { Task httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems/{id}?%24expand=1")); 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? result = JsonSerializer.Deserialize(streamTask.Result); string file = Path.Combine(targetFileLocation, $"{-9}-{id}.json"); File.WriteAllText(file, result.ToString()); } internal static void CompareWorkItems(ILogger logger, List 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); byte[] bytes = Encoding.ASCII.GetBytes($":{pat}"); VssConnection connection = new(new(string.Concat(baseAddress, basePage)), credential); MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue = new("application/json"); WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient(); HttpClient httpClient = new(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new(baseAddress) }; CompareWorkItems(logger, sourceDirectory, api, site, query, project, basePage, baseAddress, bytes, mediaTypeWithQualityHeaderValue, workItemTrackingHttpClient, httpClient); } private static ReadOnlyCollection GetWorkItems(ReadOnlyCollection valueWithReqCollection) { List results = []; Fields fields; WorkItem workItem; foreach (ValueWithReq valueWithReq in valueWithReqCollection) { fields = valueWithReq.Value.Fields; workItem = new(fields.SystemAreaPath, fields.SystemAssignedTo?.DisplayName, fields.MicrosoftVSTSCommonBusinessValue == 0 ? null : fields.MicrosoftVSTSCommonBusinessValue, fields.SystemChangedDate, fields.MicrosoftVSTSCommonClosedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonClosedDate, fields.SystemCommentCount, fields.SystemCreatedDate, fields.SystemDescription, fields.MicrosoftVSTSSchedulingEffort == 0 ? null : fields.MicrosoftVSTSSchedulingEffort, valueWithReq.Value.Id, fields.SystemIterationPath, fields.SystemParent == 0 ? null : fields.SystemParent, fields.MicrosoftVSTSCommonPriority == 0 ? null : fields.MicrosoftVSTSCommonPriority, valueWithReq.Value.Relations, fields.CustomRequester?.DisplayName, fields.MicrosoftVSTSCommonResolvedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonResolvedDate, valueWithReq.Value.Rev, fields.CustomRRminusOE == 0 ? null : fields.CustomRRminusOE, fields.MicrosoftVSTSSchedulingStartDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingStartDate, fields.SystemState, fields.SystemTags, fields.MicrosoftVSTSSchedulingTargetDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingTargetDate, fields.MicrosoftVSTSCommonTimeCriticality == 0 ? null : fields.MicrosoftVSTSCommonTimeCriticality, fields.SystemTitle, fields.SystemWorkItemType, fields.CustomWSJF == 0 ? null : fields.CustomWSJF); results.Add(workItem); } return new(results); } private static string GetIds(HttpClient httpClient, string basePage, string api, string query) { List results = []; 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, 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) { results.Add(workItem.Id); if (results.Count > 199) break; } foreach (int id in results) _ = result.Append(id).Append(','); if (result.Length > 0) _ = result.Remove(result.Length - 1, 1); return result.ToString(); } private static ReadOnlyCollection GetWorkItems(HttpClient httpClient, string basePage, string api, string targetFileLocation, string ids) { List results = []; string json; string file; Value? value; JsonElement[] jsonElements; Task httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems?ids={ids}&$expand=Relations")); 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? result = JsonSerializer.Deserialize(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; if (value.Id == 120593) GetSingle(httpClient, basePage, api, targetFileLocation, value.Id); file = Path.Combine(targetFileLocation, $"{-1}-{value.Id}.json"); File.WriteAllText(file, json); results.Add(new(value, -1, json)); } } return new(results); } private static void CompareWorkItems(WorkItemTrackingHttpClient workItemTrackingHttpClient, string sourceDirectory, string project, string site, ReadOnlyCollection valueWithReqCollection) { ArgumentNullException.ThrowIfNull(workItemTrackingHttpClient); if (string.IsNullOrEmpty(project)) throw new ArgumentException($"'{nameof(project)}' cannot be null or empty.", nameof(project)); if (string.IsNullOrEmpty(sourceDirectory)) throw new ArgumentException($"'{nameof(sourceDirectory)}' cannot be null or empty.", nameof(site)); if (string.IsNullOrEmpty(site)) throw new ArgumentException($"'{nameof(site)}' cannot be null or empty.", nameof(site)); ReadOnlyCollection workItems = GetWorkItems(valueWithReqCollection); string file = Path.Combine(sourceDirectory, $"_.json"); string json = JsonSerializer.Serialize(workItems); File.WriteAllText(file, json); foreach (WorkItem workItem in workItems) { if (workItem is null) { } } // https://stackoverflow.com/questions/18153998/how-do-i-remove-all-html-tags-from-a-string-without-knowing-which-tags-are-in-it } private static void CompareWorkItems(HttpClient httpClient, string targetFileLocation, string basePage, string api, string query, WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, string site) { string ids = GetIds(httpClient, basePage, api, query); ReadOnlyCollection valueWithReqCollection = string.IsNullOrEmpty(ids) ? new([]) : GetWorkItems(httpClient, basePage, api, targetFileLocation, ids); CompareWorkItems(workItemTrackingHttpClient, targetFileLocation, project, site, valueWithReqCollection); } #else internal static void CompareWorkItems(ILogger logger, List args) { logger.LogError("CompareWorkItems is not available in WorkItems {args[0]}", args[0]); logger.LogError("CompareWorkItems is not available in WorkItems {args[1]}", args[1]); } #endif }