diff --git a/.vscode/launch.json b/.vscode/launch.json index ff337e6..aeeb1b4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,17 +13,19 @@ "args": [ "s", "X", - "D:/5-Other-Small/Kanban-mestsa003/FI-Backlog-Mesa-Request-List/859-FI-Backlog-Mesa-Request-List/.vscode", - "Day-Helper-2024-08-06", - "1000", - "D:/Logs", - "*.txt", - "25", - "4", - "D:/IFXApps/Logs", - "8888", - "9999" - ], + "T:/MESAFIBACKLOG/06_SourceCode/MESAFIBACKLOG/Adaptation/.vscode/helper", + "Day-Helper-2024-08-09", + "MES", + "https://tfs.intra.infineon.com", + "/tfs/FactoryIntegration", + "ART SPS", + "/_apis/wit", + "/wiql/3373b300-8de3-4301-9795-e990c3b226f9", + "4n7d2jcql6bkq32f66tohddonfxajkypq66lm5y3zqemtlohawsa", + "FI Backlog Mesa - Request List.json", + "Chase|infineon\\TuckerC,Dakota(SRP)|infineon\\Mitchem,Daniel|infineon\\StieberD,Jonathan|infineon\\Ouellette,Mike|infineon\\Phares", + "Chad B|infineon\\cbecker1,Debra Q|infineon\\Quinones,Jeanne M|infineon\\jmcinty2,Jessica F|infineon\\jfuente1,Jonathon S|infineon\\jsperli1,Justin H|infineon\\jhollan2,Kelly C|infineon\\kclark1,Mark C|infineon\\mcouste1,Marti J|infineon\\mjarsey1,Nik C|infineon\\nclark1,Peyton M|infineon\\McChesne,Ron O|infineon\\Olmstead,Susan H|infineon\\HendersS,Tiffany M|infineon\\tmunoz1,Todd C|infineon\\tcarrie1" + ], "cwd": "${workspaceFolder}", "console": "integratedTerminal", "stopAtEntry": false diff --git a/Day/HelperDay.cs b/Day/HelperDay.cs index 58c06a3..57bf775 100644 --- a/Day/HelperDay.cs +++ b/Day/HelperDay.cs @@ -89,6 +89,8 @@ internal static class HelperDay Day.Q32024.Helper20240805.RenameFiles(logger, args); else if (args[1] == "Day-Helper-2024-08-06") Day.Q32024.Helper20240806.ArchiveFiles(logger, args); + else if (args[1] == "Day-Helper-2024-08-09") + Day.Q32024.Helper20240809.CreateWorkItems(logger, args); else throw new Exception(appSettings.Company); } diff --git a/Day/Q32024/ConvertExcelToJson/FIBacklogMesa.cs b/Day/Q32024/ConvertExcelToJson/FIBacklogMesa.cs new file mode 100644 index 0000000..becba61 --- /dev/null +++ b/Day/Q32024/ConvertExcelToJson/FIBacklogMesa.cs @@ -0,0 +1,144 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.ConvertExcelToJson; + +public class FIBacklogMesa +{ + [JsonConstructor] + public FIBacklogMesa(string req, + string submitted, + string requestor, + string assignedTo, + string secondResource, + string subject, + string systemS, + string priority, + string training, + string prioritySubset, + string status, + string definition, + string updates, + string estEffortDays, + string commitDate, + string reCommitDate, + string uATAsOf, + string cmpDate, + string f20, + string f21, + string f22, + string f23, + string f24, + string f25, + string f26, + string f27, + string f28, + string f29, + string f30, + string f31, + string f32, + string f33) + { + Req = req; + Submitted = submitted; + Requestor = requestor; + AssignedTo = assignedTo; + SecondResource = secondResource; + Subject = subject; + SystemS = systemS; + Priority = priority; + Training = training; + PrioritySubset = prioritySubset; + Status = status; + Definition = definition; + Updates = updates; + EstEffortDays = estEffortDays; + CommitDate = commitDate; + ReCommitDate = reCommitDate; + UATAsOf = uATAsOf; + CMPDate = cmpDate; + F20 = f20; + F21 = f21; + F22 = f22; + F23 = f23; + F24 = f24; + F25 = f25; + F26 = f26; + F27 = f27; + F28 = f28; + F29 = f29; + F30 = f30; + F31 = f31; + F32 = f32; + F33 = f33; + } + + public string Req { get; set; } // { init; get; } + public string Submitted { get; set; } // { init; get; } + public string Requestor { get; set; } // { init; get; } + + [JsonPropertyName("Assigned To")] + public string AssignedTo { get; set; } // { init; get; } + + [JsonPropertyName("Second Resource")] + public string SecondResource { get; set; } // { init; get; } + + [JsonPropertyName("Subject - from Requestor")] + public string Subject { get; set; } // { init; get; } + + [JsonPropertyName("System(s)")] + public string SystemS { get; set; } // { init; get; } + + public string Priority { get; set; } // { init; get; } + + [JsonPropertyName("Spec/ECN/Training")] + public string Training { get; set; } // { init; get; } + + [JsonPropertyName("Qual/Eff")] + public string PrioritySubset { get; set; } // { init; get; } + public string Status { get; set; } // { init; get; } + + [JsonPropertyName("Definition - from FI")] + public string Definition { get; set; } // { init; get; } + public string Updates { get; set; } // { init; get; } + + [JsonPropertyName("Est Effort _(days)")] + public string EstEffortDays { get; set; } // { init; get; } + + [JsonPropertyName("Commit Date")] + public string CommitDate { get; set; } // { init; get; } + + [JsonPropertyName("Re-Commit Date")] + public string ReCommitDate { get; set; } // { init; get; } + + [JsonPropertyName("UAT as of")] + public string UATAsOf { get; set; } // { init; get; } + + [JsonPropertyName("CMP _Date")] + public string CMPDate { get; set; } // { init; get; } + public string F20 { get; set; } // { init; get; } + public string F21 { get; set; } // { init; get; } + public string F22 { get; set; } // { init; get; } + public string F23 { get; set; } // { init; get; } + public string F24 { get; set; } // { init; get; } + public string F25 { get; set; } // { init; get; } + public string F26 { get; set; } // { init; get; } + public string F27 { get; set; } // { init; get; } + public string F28 { get; set; } // { init; get; } + public string F29 { get; set; } // { init; get; } + public string F30 { get; set; } // { init; get; } + public string F31 { get; set; } // { init; get; } + public string F32 { get; set; } // { init; get; } + public string F33 { get; set; } // { init; get; } +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)] +[JsonSerializable(typeof(FIBacklogMesa[]))] +internal partial class FIBacklogMesaCollectionSourceGenerationContext : JsonSerializerContext +{ +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)] +[JsonSerializable(typeof(FIBacklogMesa))] +internal partial class FIBacklogMesaSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Day/Q32024/Helper-2024-08-09.cs b/Day/Q32024/Helper-2024-08-09.cs new file mode 100644 index 0000000..4eaa3fa --- /dev/null +++ b/Day/Q32024/Helper-2024-08-09.cs @@ -0,0 +1,560 @@ +using File_Folder_Helper.Day.Q32024.ConvertExcelToJson; +using File_Folder_Helper.Day.Q32024.WorkItems; +using Microsoft.Extensions.Logging; +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; + +namespace File_Folder_Helper.Day.Q32024; + +internal static partial class Helper20240809 +{ + + 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 = []; + 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 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) + _ = result.Append(workItem.Id).Append(','); + if (result.Length > 0) + _ = result.Remove(result.Length - 1, 1); + return result.ToString(); + } + + private static ReadOnlyCollection GetWorkItems(HttpClient httpClient, string sourceDirectory, string basePage, string api, string ids) + { + List results = []; + int req; + string json; + string file; + Value? value; + string[] segments; + 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)); + JsonElement[] jsonElements = result.Value.EnumerateObject().Last().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}.json"); + File.WriteAllText(file, json); + results.Add(new(value, req, json)); + } + return new(results); + } + + private static ReadOnlyCollection RemoveFrom(Dictionary keyToFIBacklogMesa, ReadOnlyCollection valueWithReqCollection) + { + List results = []; + foreach (ValueWithReq valueWithReq in valueWithReqCollection) + { + if (keyToFIBacklogMesa.Remove($"{valueWithReq.Req} - ")) + continue; + results.Add(valueWithReq); + } + return new(results); + } + + 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 assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, Task? 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); + 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}
 
{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 requestorNameToUser, ReadOnlyDictionary assignedToNameToUser, List tags, Task? 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}
 
{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); + 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 GetTags(FIBacklogMesa fiBacklogMesa) + { + List results = []; + 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 = []; + 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 assignedToNameToUser, ReadOnlyDictionary requestorNameToUser, FIBacklogMesa fiBacklogMesa) + { + DateTime submittedDateTime; + JsonPatchDocument tagDocument; + Task? workItem = null; + List 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? uatWorkItemTask = null; + JsonPatchDocument bugDocument = GetBugDocument(project, site, requestorNameToUser, assignedToNameToUser, uatWorkItemTask, fiBacklogMesa, submittedDateTime); + workItem = workItemTrackingHttpClient.CreateWorkItemAsync(bugDocument, project, "Bug"); + workItem.Wait(); + } + if (!isBugFix) + { + Task? 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 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 + 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 = 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 assignedToNameToUser, + ReadOnlyDictionary requestorNameToUser, + Dictionary keyToFIBacklogMesa, + ReadOnlyCollection valueWithReqCollection) + { + int result = 0; + 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.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.Effort != estEffortDays) + { + result += 1; + Update(httpClient, basePage, api, 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) + { + 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 assignedToNameToUser, + ReadOnlyDictionary requestorNameToUser, + string json) + { + int counter = 0; + string ids = GetIds(httpClient, basePage, api, query); + Dictionary keyToFIBacklogMesa = GetFIBacklogMesaCollection(json); + ReadOnlyCollection valueWithReqCollection = string.IsNullOrEmpty(ids) ? new([]) : GetWorkItems(httpClient, sourceDirectory, basePage, api, ids); + int updated = SetSyncTag(httpClient, basePage, api, workItemTrackingHttpClient, assignedToNameToUser, requestorNameToUser, keyToFIBacklogMesa, valueWithReqCollection); + if (updated == 0) + { + ReadOnlyCollection extra = RemoveFrom(keyToFIBacklogMesa, valueWithReqCollection); + foreach (KeyValuePair keyValuePair in keyToFIBacklogMesa) + { + if (keyToFIBacklogMesa.Count == extra.Count) + break; + CreateWorkItem(workItemTrackingHttpClient, project, site, assignedToNameToUser, requestorNameToUser, keyValuePair.Value); + counter++; + } + } + + } + + private static void CreateWorkItems(ILogger 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 requestorNameToUser = GetRequestorNameToUser(requestorNames); + ReadOnlyDictionary 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 GetAssignedToNameToUser(string[] assignedToNames) + { + Dictionary 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 GetRequestorNameToUser(string[] requestorNames) + { + Dictionary 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 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); + 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(); + 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); + } + +} \ No newline at end of file diff --git a/Day/Q32024/WIQL/Column.cs b/Day/Q32024/WIQL/Column.cs new file mode 100644 index 0000000..da7f88f --- /dev/null +++ b/Day/Q32024/WIQL/Column.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WIQL; + +public class Column +{ + [JsonConstructor] + public Column( + string referenceName, + string name, + string url + ) + { + ReferenceName = referenceName; + Name = name; + Url = url; + } + + public string ReferenceName { get; set; } // { init; get; } + public string Name { get; set; } // { init; get; } + public string Url { get; set; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WIQL/Field.cs b/Day/Q32024/WIQL/Field.cs new file mode 100644 index 0000000..3994aa5 --- /dev/null +++ b/Day/Q32024/WIQL/Field.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WIQL; + +public class Field +{ + [JsonConstructor] + public Field( + string referenceName, + string name, + string url + ) + { + ReferenceName = referenceName; + Name = name; + Url = url; + } + + public string ReferenceName { get; set; } // { init; get; } + public string Name { get; set; } // { init; get; } + public string Url { get; set; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WIQL/Root.cs b/Day/Q32024/WIQL/Root.cs new file mode 100644 index 0000000..c98a82d --- /dev/null +++ b/Day/Q32024/WIQL/Root.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WIQL; + +public class Root +{ + [JsonConstructor] + public Root( + string queryType, + string queryResultType, + DateTime asOf, + Column[] columns, + SortColumn[] sortColumns, + WorkItem[] workItems + ) + { + QueryType = queryType; + QueryResultType = queryResultType; + AsOf = asOf; + Columns = columns; + SortColumns = sortColumns; + WorkItems = workItems; + } + + public string QueryType { get; set; } // { init; get; } + public string QueryResultType { get; set; } // { init; get; } + public DateTime AsOf { get; set; } // { init; get; } + public Column[] Columns { get; set; } // { init; get; } + public SortColumn[] SortColumns { get; set; } // { init; get; } + public WorkItem[] WorkItems { get; set; } // { init; get; } +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)] +[JsonSerializable(typeof(Root))] +internal partial class WIQLRootSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Day/Q32024/WIQL/SortColumn.cs b/Day/Q32024/WIQL/SortColumn.cs new file mode 100644 index 0000000..33e14ff --- /dev/null +++ b/Day/Q32024/WIQL/SortColumn.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WIQL; + +public class SortColumn +{ + [JsonConstructor] + public SortColumn( + Field field, + bool descending + ) + { + Field = field; + Descending = descending; + } + + public Field Field { get; set; } // { init; get; } + public bool Descending { get; set; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WIQL/WorkItem.cs b/Day/Q32024/WIQL/WorkItem.cs new file mode 100644 index 0000000..4087d51 --- /dev/null +++ b/Day/Q32024/WIQL/WorkItem.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WIQL; + +public class WorkItem +{ + [JsonConstructor] + public WorkItem( + int id, + string url + ) + { + Id = id; + Url = url; + } + + public int Id { get; set; } // { init; get; } + public string Url { get; set; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/Avatar.cs b/Day/Q32024/WorkItems/Avatar.cs new file mode 100644 index 0000000..d733b73 --- /dev/null +++ b/Day/Q32024/WorkItems/Avatar.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class Avatar +{ + [JsonConstructor] + public Avatar( + string href + ) => Href = href; + + public string Href { get; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/CommentVersionRef.cs b/Day/Q32024/WorkItems/CommentVersionRef.cs new file mode 100644 index 0000000..11b0c7e --- /dev/null +++ b/Day/Q32024/WorkItems/CommentVersionRef.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class CommentVersionRef +{ + [JsonConstructor] + public CommentVersionRef( + int commentId, + int version, + string url + ) + { + CommentId = commentId; + Version = version; + URL = url; + } + + [JsonPropertyName("commentId")] + public int CommentId { get; } // { init; get; } + + [JsonPropertyName("version")] + public int Version { get; } // { init; get; } + + [JsonPropertyName("url")] + public string URL { get; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/CustomRequester.cs b/Day/Q32024/WorkItems/CustomRequester.cs new file mode 100644 index 0000000..e83597d --- /dev/null +++ b/Day/Q32024/WorkItems/CustomRequester.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class CustomRequester +{ + [JsonConstructor] + public CustomRequester( + string displayName, + string url, + Links links, + string id, + string uniqueName, + string imageUrl, + string descriptor + ) + { + DisplayName = displayName; + Url = url; + Links = links; + Id = id; + UniqueName = uniqueName; + ImageUrl = imageUrl; + Descriptor = descriptor; + } + + [JsonPropertyName("displayName")] + public string DisplayName { get; } + + [JsonPropertyName("url")] + public string Url { get; } + + [JsonPropertyName("_links")] + public Links Links { get; } + + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("uniqueName")] + public string UniqueName { get; } + + [JsonPropertyName("imageUrl")] + public string ImageUrl { get; } + + [JsonPropertyName("descriptor")] + public string Descriptor { get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/Fields.cs b/Day/Q32024/WorkItems/Fields.cs new file mode 100644 index 0000000..17b2974 --- /dev/null +++ b/Day/Q32024/WorkItems/Fields.cs @@ -0,0 +1,117 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class Fields +{ + [JsonConstructor] + public Fields( + string systemAreaPath, + string systemTeamProject, + string systemIterationPath, + string systemWorkItemType, + string systemState, + string systemReason, + CustomRequester customRequester, + SystemAssignedTo systemAssignedTo, + DateTime systemCreatedDate, + SystemCreatedBy systemCreatedBy, + DateTime systemChangedDate, + SystemChangedBy systemChangedBy, + int systemCommentCount, + string systemTitle, + DateTime microsoftVSTSCommonStateChangeDate, + int microsoftVSTSCommonPriority, + string systemDescription, + string systemTags, + string systemHistory, + float? effort, + DateTime targetDate + ) + { + SystemAreaPath = systemAreaPath; + SystemTeamProject = systemTeamProject; + SystemIterationPath = systemIterationPath; + SystemWorkItemType = systemWorkItemType; + SystemState = systemState; + SystemReason = systemReason; + CustomRequester = customRequester; + SystemAssignedTo = systemAssignedTo; + SystemCreatedDate = systemCreatedDate; + SystemCreatedBy = systemCreatedBy; + SystemChangedDate = systemChangedDate; + SystemChangedBy = systemChangedBy; + SystemCommentCount = systemCommentCount; + SystemTitle = systemTitle; + MicrosoftVSTSCommonStateChangeDate = microsoftVSTSCommonStateChangeDate; + MicrosoftVSTSCommonPriority = microsoftVSTSCommonPriority; + SystemDescription = systemDescription; + SystemTags = systemTags; + SystemHistory = systemHistory; + Effort = effort; + TargetDate = targetDate; + } + + [JsonPropertyName("System.AreaPath")] + public string SystemAreaPath { get; } // { init; get; } + + [JsonPropertyName("System.TeamProject")] + public string SystemTeamProject { get; } // { init; get; } + + [JsonPropertyName("System.IterationPath")] + public string SystemIterationPath { get; } // { init; get; } + + [JsonPropertyName("System.WorkItemType")] + public string SystemWorkItemType { get; } // { init; get; } + + [JsonPropertyName("System.State")] + public string SystemState { get; } // { init; get; } + + [JsonPropertyName("System.Reason")] + public string SystemReason { get; } // { init; get; } + + [JsonPropertyName("Custom.Requester")] + public CustomRequester CustomRequester { get; } // { init; get; } + + [JsonPropertyName("System.AssignedTo")] + public SystemAssignedTo SystemAssignedTo { get; } // { init; get; } + + [JsonPropertyName("System.CreatedDate")] + public DateTime SystemCreatedDate { get; } // { init; get; } + + [JsonPropertyName("System.CreatedBy")] + public SystemCreatedBy SystemCreatedBy { get; } // { init; get; } + + [JsonPropertyName("System.ChangedDate")] + public DateTime SystemChangedDate { get; } // { init; get; } + + [JsonPropertyName("System.ChangedBy")] + public SystemChangedBy SystemChangedBy { get; } // { init; get; } + + [JsonPropertyName("System.CommentCount")] + public int SystemCommentCount { get; } // { init; get; } + + [JsonPropertyName("System.Title")] + public string SystemTitle { get; } // { init; get; } + + [JsonPropertyName("Microsoft.VSTS.Common.StateChangeDate")] + public DateTime MicrosoftVSTSCommonStateChangeDate { get; } // { init; get; } + + [JsonPropertyName("Microsoft.VSTS.Common.Priority")] + public int MicrosoftVSTSCommonPriority { get; } // { init; get; } + + [JsonPropertyName("System.Description")] + public string SystemDescription { get; } // { init; get; } + + [JsonPropertyName("System.Tags")] + public string SystemTags { get; } // { init; get; } + + [JsonPropertyName("System.History")] + public string SystemHistory { get; } // { init; get; } + + [JsonPropertyName("Microsoft.VSTS.Scheduling.Effort")] + public float? Effort { get; } // { init; get; } + + [JsonPropertyName("Microsoft.VSTS.Scheduling.TargetDate")] + public DateTime TargetDate { get; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/Html.cs b/Day/Q32024/WorkItems/Html.cs new file mode 100644 index 0000000..c414b01 --- /dev/null +++ b/Day/Q32024/WorkItems/Html.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class Html +{ + [JsonConstructor] + public Html( + string href + ) => Href = href; + + public string Href { get; } // { init; get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/Links.cs b/Day/Q32024/WorkItems/Links.cs new file mode 100644 index 0000000..91bb0ec --- /dev/null +++ b/Day/Q32024/WorkItems/Links.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class Links +{ + [JsonConstructor] + public Links( + Avatar avatar + ) => Avatar = avatar; + + [JsonPropertyName("avatar")] + public Avatar Avatar { get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/SystemAssignedTo.cs b/Day/Q32024/WorkItems/SystemAssignedTo.cs new file mode 100644 index 0000000..6adf916 --- /dev/null +++ b/Day/Q32024/WorkItems/SystemAssignedTo.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class SystemAssignedTo +{ + [JsonConstructor] + public SystemAssignedTo( + string displayName, + string url, + Links links, + string id, + string uniqueName, + string imageUrl, + string descriptor + ) + { + DisplayName = displayName; + Url = url; + Links = links; + Id = id; + UniqueName = uniqueName; + ImageUrl = imageUrl; + Descriptor = descriptor; + } + + [JsonPropertyName("displayName")] + public string DisplayName { get; } + + [JsonPropertyName("url")] + public string Url { get; } + + [JsonPropertyName("_links")] + public Links Links { get; } + + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("uniqueName")] + public string UniqueName { get; } + + [JsonPropertyName("imageUrl")] + public string ImageUrl { get; } + + [JsonPropertyName("descriptor")] + public string Descriptor { get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/SystemChangedBy.cs b/Day/Q32024/WorkItems/SystemChangedBy.cs new file mode 100644 index 0000000..31ba222 --- /dev/null +++ b/Day/Q32024/WorkItems/SystemChangedBy.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class SystemChangedBy +{ + [JsonConstructor] + public SystemChangedBy( + string displayName, + string url, + Links links, + string id, + string uniqueName, + string imageUrl, + string descriptor + ) + { + DisplayName = displayName; + Url = url; + Links = links; + Id = id; + UniqueName = uniqueName; + ImageUrl = imageUrl; + Descriptor = descriptor; + } + + [JsonPropertyName("displayName")] + public string DisplayName { get; } + + [JsonPropertyName("url")] + public string Url { get; } + + [JsonPropertyName("_links")] + public Links Links { get; } + + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("uniqueName")] + public string UniqueName { get; } + + [JsonPropertyName("imageUrl")] + public string ImageUrl { get; } + + [JsonPropertyName("descriptor")] + public string Descriptor { get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/SystemCreatedBy.cs b/Day/Q32024/WorkItems/SystemCreatedBy.cs new file mode 100644 index 0000000..3659480 --- /dev/null +++ b/Day/Q32024/WorkItems/SystemCreatedBy.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class SystemCreatedBy +{ + [JsonConstructor] + public SystemCreatedBy( + string displayName, + string url, + Links links, + string id, + string uniqueName, + string imageUrl, + string descriptor + ) + { + DisplayName = displayName; + Url = url; + Links = links; + Id = id; + UniqueName = uniqueName; + ImageUrl = imageUrl; + Descriptor = descriptor; + } + + [JsonPropertyName("displayName")] + public string DisplayName { get; } + + [JsonPropertyName("url")] + public string Url { get; } + + [JsonPropertyName("_links")] + public Links Links { get; } + + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("uniqueName")] + public string UniqueName { get; } + + [JsonPropertyName("imageUrl")] + public string ImageUrl { get; } + + [JsonPropertyName("descriptor")] + public string Descriptor { get; } +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/Value.cs b/Day/Q32024/WorkItems/Value.cs new file mode 100644 index 0000000..86cfc17 --- /dev/null +++ b/Day/Q32024/WorkItems/Value.cs @@ -0,0 +1,49 @@ +using System.Text.Json.Serialization; + +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class Value +{ + [JsonConstructor] + public Value( + int id, + int rev, + Fields fields, + CommentVersionRef commentVersionRef, + string url + ) + { + Id = id; + Rev = rev; + Fields = fields; + CommentVersionRef = commentVersionRef; + Url = url; + } + + [JsonPropertyName("id")] + public int Id { get; } + + [JsonPropertyName("rev")] + public int Rev { get; } + + [JsonPropertyName("fields")] + public Fields Fields { get; } + + [JsonPropertyName("commentVersionRef")] + public CommentVersionRef CommentVersionRef { get; } + + [JsonPropertyName("url")] + public string Url { get; } +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)] +[JsonSerializable(typeof(Value[]))] +internal partial class ValueCollectionSourceGenerationContext : JsonSerializerContext +{ +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)] +[JsonSerializable(typeof(Value))] +internal partial class ValueSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Day/Q32024/WorkItems/ValueWithReq.cs b/Day/Q32024/WorkItems/ValueWithReq.cs new file mode 100644 index 0000000..9529e72 --- /dev/null +++ b/Day/Q32024/WorkItems/ValueWithReq.cs @@ -0,0 +1,19 @@ +namespace File_Folder_Helper.Day.Q32024.WorkItems; + +public class ValueWithReq +{ + public ValueWithReq( + Value value, + int req, + string json + ) + { + Value = value; + Req = req; + Json = json; + } + + public Value Value { get; set; } // { init; get; } + public int Req { get; set; } // { init; get; } + public string Json { get; set; } // { init; get; } +} \ No newline at end of file diff --git a/File-Folder-Helper.csproj b/File-Folder-Helper.csproj index f90af22..ea0b4bc 100644 --- a/File-Folder-Helper.csproj +++ b/File-Folder-Helper.csproj @@ -1,4 +1,4 @@ - + enable Exe @@ -16,6 +16,7 @@ +