using File_Folder_Helper.Day.Q32024.WorkItems; using Microsoft.Extensions.Logging; 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; namespace File_Folder_Helper.Day.Q32024; internal static partial class Helper20240830 { public record WorkItem(string? AssignedTo, int? BusinessValue, DateTime ChangedDate, int CommentCount, DateTime CreatedDate, string Description, float? Effort, int Id, int? Priority, string? Requester, int Revision, int? RiskReductionMinusOpportunityEnablement, DateTime? StartDate, string State, string Tags, DateTime? TargetDate, float? TimeCriticality, string Title, string WorkItemType, float? WeightedShortestJobFirst); 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}")); 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; file = Path.Combine(targetFileLocation, $"{-1}-{value.Id}.json"); File.WriteAllText(file, json); results.Add(new(value, -1, json)); } } return new(results); } private static ReadOnlyCollection GetWorkItems(ReadOnlyCollection valueWithReqCollection) { List results = []; Fields fields; WorkItem workItem; foreach (ValueWithReq valueWithReq in valueWithReqCollection) { fields = valueWithReq.Value.Fields; workItem = new(fields.SystemAssignedTo?.DisplayName, fields.MicrosoftVSTSCommonBusinessValue == 0 ? null : fields.MicrosoftVSTSCommonBusinessValue, fields.SystemChangedDate, fields.SystemCommentCount, fields.SystemCreatedDate, fields.SystemDescription, fields.MicrosoftVSTSSchedulingEffort == 0 ? null : fields.MicrosoftVSTSSchedulingEffort, valueWithReq.Value.Id, fields.MicrosoftVSTSCommonPriority == 0 ? null : fields.MicrosoftVSTSCommonPriority, fields.CustomRequester?.DisplayName, 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 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); } 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); } 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); } }