From 0505330ac5a8ba9c5fb1a1c150f021db42ba5820 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Wed, 13 Nov 2024 14:09:12 -0700 Subject: [PATCH] Try Catch on Serialize Better Relation Data --- Adaptation/FileHandlers/ADO/ProcessData.cs | 2 +- .../DownloadWorkItems/FileRead.cs | 10 +- Adaptation/FileHandlers/Dummy/FileRead.cs | 15 ++- Adaptation/FileHandlers/Kanban/ProcessData.cs | 50 +++++++--- .../FileHandlers/Markdown/ProcessData.cs | 47 +++++++--- .../FileHandlers/Priority/Aggregation.cs | 6 ++ Adaptation/FileHandlers/Priority/FileRead.cs | 10 +- Adaptation/FileHandlers/json/FileRead.cs | 91 +++++++++++++++++-- .../FileHandlers/json/WIQL/Attribute.cs | 25 +++++ Adaptation/FileHandlers/json/WIQL/Relation.cs | 35 +++++++ .../FileHandlers/json/WorkItems/Attribute.cs | 13 ++- .../FileHandlers/json/WorkItems/Record.cs | 41 +++------ .../FileHandlers/json/WorkItems/Relation.cs | 12 +-- .../FileHandlers/json/WorkItems/Value.cs | 4 +- .../FileHandlers/json/WorkItems/WorkItem.cs | 33 +++++++ .../Extract/Development/v2.57.0/BACKLOG.cs | 2 +- MESAFIBACKLOG.csproj | 2 + 17 files changed, 322 insertions(+), 76 deletions(-) create mode 100644 Adaptation/FileHandlers/json/WIQL/Attribute.cs create mode 100644 Adaptation/FileHandlers/json/WIQL/Relation.cs diff --git a/Adaptation/FileHandlers/ADO/ProcessData.cs b/Adaptation/FileHandlers/ADO/ProcessData.cs index 8e740f1..9c7639f 100644 --- a/Adaptation/FileHandlers/ADO/ProcessData.cs +++ b/Adaptation/FileHandlers/ADO/ProcessData.cs @@ -145,9 +145,9 @@ public class ProcessData : IProcessData try { childRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Child", nests, keepRelations); // Forward - // records = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse relatedRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Related", nests, keepRelations); // Related successorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Successor", nests, keepRelations); // Forward + // predecessorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse record = Record.Get(keyValuePair.Value, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations); } catch (Exception) diff --git a/Adaptation/FileHandlers/DownloadWorkItems/FileRead.cs b/Adaptation/FileHandlers/DownloadWorkItems/FileRead.cs index d2dd3df..991cf42 100644 --- a/Adaptation/FileHandlers/DownloadWorkItems/FileRead.cs +++ b/Adaptation/FileHandlers/DownloadWorkItems/FileRead.cs @@ -223,7 +223,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } try @@ -238,7 +241,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } } diff --git a/Adaptation/FileHandlers/Dummy/FileRead.cs b/Adaptation/FileHandlers/Dummy/FileRead.cs index 916f5d7..530c308 100644 --- a/Adaptation/FileHandlers/Dummy/FileRead.cs +++ b/Adaptation/FileHandlers/Dummy/FileRead.cs @@ -137,7 +137,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } File.AppendAllLines(traceDummyFile, new string[] { site, monARessource, stateName, State.Critical.ToString(), exception.Message, exception.StackTrace }); _ = monIn.SendStatus(site, monARessource, stateName, State.Critical); @@ -265,7 +268,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } try @@ -278,7 +284,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } } diff --git a/Adaptation/FileHandlers/Kanban/ProcessData.cs b/Adaptation/FileHandlers/Kanban/ProcessData.cs index a006a9a..7675bf1 100644 --- a/Adaptation/FileHandlers/Kanban/ProcessData.cs +++ b/Adaptation/FileHandlers/Kanban/ProcessData.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; @@ -71,7 +72,7 @@ public class ProcessData : IProcessData _ = Directory.CreateDirectory(destinationDirectory); ReadOnlyDictionary keyValuePairs = GetWorkItems(workItems, keepRelations); ReadOnlyCollection bugUserStoryTaskWorkItemTypes = new(new string[] { "Bug", "User Story", "Task" }); - ReadOnlyDictionary collection = MoveCurrentAndGetExpectedDirectories(taskWorkItemType, destinationDirectory, bugUserStoryTaskWorkItemTypes, keyValuePairs); + ReadOnlyDictionary collection = MoveCurrentAndGetExpectedDirectoriesAndFileCopy(taskWorkItemType, destinationDirectory, json, bugUserStoryTaskWorkItemTypes, keyValuePairs); WriteFiles(fileRead, fileInfoCollection, taskWorkItemType, destinationDirectory, keyValuePairs, collection); } @@ -85,12 +86,12 @@ public class ProcessData : IProcessData return results; } - private static ReadOnlyDictionary MoveCurrentAndGetExpectedDirectories(string taskWorkItemType, string destinationDirectory, ReadOnlyCollection bugUserStoryTaskWorkItemTypes, ReadOnlyDictionary keyValuePairs) + private static ReadOnlyDictionary MoveCurrentAndGetExpectedDirectoriesAndFileCopy(string taskWorkItemType, string destinationDirectory, string json, ReadOnlyCollection bugUserStoryTaskWorkItemTypes, ReadOnlyDictionary keyValuePairs) { ReadOnlyDictionary results; string? directory; ReadOnlyDictionary collection = GetCurrentDirectories(destinationDirectory, bugUserStoryTaskWorkItemTypes); - results = GetExpectedDirectories(taskWorkItemType, destinationDirectory, bugUserStoryTaskWorkItemTypes, keyValuePairs); + results = GetExpectedDirectoriesAndFileCopy(taskWorkItemType, destinationDirectory, json, bugUserStoryTaskWorkItemTypes, keyValuePairs); foreach (KeyValuePair keyValuePair in collection) { if (!results.TryGetValue(keyValuePair.Key, out directory)) @@ -109,12 +110,18 @@ public class ProcessData : IProcessData private static FileInfo GetFileInfoAndMaybeWriteFile(string directory, WorkItem workItem) { FileInfo result; - string json; - json = JsonSerializer.Serialize(workItem, WorkItemSourceGenerationContext.Default.WorkItem); + string json = JsonSerializer.Serialize(workItem, WorkItemSourceGenerationContext.Default.WorkItem); string singletonDirectory = Path.Combine(directory, $"{workItem.Id}"); - if (!Directory.Exists(singletonDirectory)) - _ = Directory.CreateDirectory(singletonDirectory); - result = new(Path.Combine(singletonDirectory, ".json")); + if (Directory.Exists(singletonDirectory)) + { + List files = Directory.GetFiles(singletonDirectory, "*", SearchOption.AllDirectories).ToList(); + string checkFile = Path.Combine(singletonDirectory, ".json"); + if (files.Remove(checkFile)) + File.Delete(checkFile); + if (files.Count == 0) + Directory.Delete(singletonDirectory); + } + result = new(Path.Combine(directory, $"{workItem.Id}.json")); string old = result.Exists ? File.ReadAllText(result.FullName) : string.Empty; if (old != json) File.WriteAllText(result.FullName, json); @@ -126,7 +133,6 @@ public class ProcessData : IProcessData string? directory; FileInfo fileInfo; WorkItem workItem; - Record? parent; foreach (KeyValuePair keyValuePair in keyValuePairs) { workItem = keyValuePair.Value.WorkItem; @@ -139,8 +145,6 @@ public class ProcessData : IProcessData fileInfo = GetFileInfoAndMaybeWriteFile(directory, workItem); if (!fileRead.IsEAFHosted) fileInfoCollection.Add(fileInfo); - if (workItem.WorkItemType != taskWorkItemType && workItem.Parent is not null && keyValuePairs.TryGetValue(workItem.Parent.Value, out parent)) - _ = GetFileInfoAndMaybeWriteFile(Path.Combine(directory, ".parent"), parent.WorkItem); } } @@ -163,9 +167,9 @@ public class ProcessData : IProcessData try { childRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Child", nests, keepRelations); // Forward - // records = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse relatedRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Related", nests, keepRelations); // Related successorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Successor", nests, keepRelations); // Forward + // predecessorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse record = Record.Get(keyValuePair.Value, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations); } catch (Exception) @@ -205,14 +209,32 @@ public class ProcessData : IProcessData return new(results); } - private static ReadOnlyDictionary GetExpectedDirectories(string taskWorkItemType, string destinationDirectory, ReadOnlyCollection bugUserStoryTaskWorkItemTypes, ReadOnlyDictionary keyValuePairs) + private static void FileCopy(string destinationDirectory, string json, List distinct) + { + string old; + string checkFile; + foreach (string iterationPath in distinct) + { + checkFile = Path.Combine(destinationDirectory, iterationPath, "[].json"); + old = File.Exists(checkFile) ? File.ReadAllText(checkFile) : string.Empty; + if (old != json) + File.WriteAllText(checkFile, json); + } + } + + private static ReadOnlyDictionary GetExpectedDirectoriesAndFileCopy(string taskWorkItemType, string destinationDirectory, string json, ReadOnlyCollection bugUserStoryTaskWorkItemTypes, ReadOnlyDictionary keyValuePairs) { Dictionary results = new(); string directory; WorkItem workItem; + string iterationPath; + List distinct = new(); foreach (KeyValuePair keyValuePair in keyValuePairs) { workItem = keyValuePair.Value.WorkItem; + iterationPath = workItem.IterationPath.Replace(" ", "-"); + if (!distinct.Contains(iterationPath)) + distinct.Add(iterationPath); if (!bugUserStoryTaskWorkItemTypes.Contains(workItem.WorkItemType)) continue; if (workItem.WorkItemType == taskWorkItemType && workItem.Parent is not null) @@ -220,6 +242,8 @@ public class ProcessData : IProcessData directory = GetDirectory(destinationDirectory, workItem); results.Add(workItem.Id, directory); } + if (distinct.Count > 0) + FileCopy(destinationDirectory, json, distinct); return new(results); } diff --git a/Adaptation/FileHandlers/Markdown/ProcessData.cs b/Adaptation/FileHandlers/Markdown/ProcessData.cs index 420e4df..4603760 100644 --- a/Adaptation/FileHandlers/Markdown/ProcessData.cs +++ b/Adaptation/FileHandlers/Markdown/ProcessData.cs @@ -3,7 +3,6 @@ using Adaptation.Shared; using Adaptation.Shared.Duplicator; using Adaptation.Shared.Methods; using log4net; -using Microsoft.VisualStudio.Services.Common; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -81,15 +80,16 @@ public class ProcessData : IProcessData List spaces = new(); bool keepRelations = false; List lines = new(); + List messages = new(); ReadOnlyCollection results; ReadOnlyDictionary keyValuePairs = GetWorkItems(workItems, keepRelations); ReadOnlyCollection records = new(keyValuePairs.Values.ToArray()); - WriteFile(fileRead, destinationDirectory, fileInfoCollection, records, "records"); ReadOnlyCollection bugFeatureWorkItemTypes = new(new string[] { "Bug", "Feature" }); ReadOnlyCollection bugUserStoryWorkItemTypes = new(new string[] { "Bug", "User Story" }); ReadOnlyCollection bugUserStoryTaskWorkItemTypes = new(new string[] { "Bug", "User Story", "Task" }); - WriteWithPartentsFile(fileRead, destinationDirectory, fileInfoCollection, records, bugFeatureWorkItemTypes, "bugs-features-with-parents"); - WriteWithPartentsFile(fileRead, destinationDirectory, fileInfoCollection, records, bugUserStoryWorkItemTypes, "bugs-user-stories-with-parents"); + messages.AddRange(WriteFile(fileRead, destinationDirectory, fileInfoCollection, records, "records")); + messages.AddRange(WriteWithPartentsFile(fileRead, destinationDirectory, fileInfoCollection, records, bugFeatureWorkItemTypes, "bugs-features-with-parents")); + messages.AddRange(WriteWithPartentsFile(fileRead, destinationDirectory, fileInfoCollection, records, bugUserStoryWorkItemTypes, "bugs-user-stories-with-parents")); foreach (string workItemType in workItemTypes) { lines.Clear(); @@ -154,6 +154,8 @@ public class ProcessData : IProcessData WriteFiles(fileRead, destinationDirectory, fileInfoCollection, new(lines), results, "check-122517"); _Details.Add(results); } + if (messages.Count > 0) + throw new Exception($"{messages.Count}{Environment.NewLine}{string.Join(Environment.NewLine, messages)}"); } private static void WriteFiles(IFileRead fileRead, string destinationDirectory, List fileInfoCollection, ReadOnlyCollection lines, ReadOnlyCollection records, string fileName) @@ -191,19 +193,41 @@ public class ProcessData : IProcessData return results; } - private static void WriteFile(IFileRead fileRead, string destinationDirectory, List fileInfoCollection, ReadOnlyCollection records, string fileName) + private static string? GetJson(IEnumerable records, List results) { - string json = JsonSerializer.Serialize(records, new JsonSerializerOptions() { WriteIndented = true }); + string? result; + try + { result = JsonSerializer.Serialize(records.ToArray(), RecordCollectionSourceGenerationContext.Default.RecordArray); } + catch (Exception) + { + result = null; + foreach (Record record in records) + { + try + { _ = JsonSerializer.Serialize(record, RecordSourceGenerationContext.Default.Record); } + catch (Exception ex) + { results.Add($"Record {record.WorkItem.Id} is not serializable!{Environment.NewLine}{ex.Message}"); } + } + } + return result; + } + + private static ReadOnlyCollection WriteFile(IFileRead fileRead, string destinationDirectory, List fileInfoCollection, ReadOnlyCollection records, string fileName) + { + List results = new(); + string? json = GetJson(records, results); string jsonFile = Path.Combine(destinationDirectory, $"{fileName}.json"); string jsonOld = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile); - if (json != jsonOld) + if (!string.IsNullOrEmpty(json) && json != jsonOld) File.WriteAllText(jsonFile, json); if (!fileRead.IsEAFHosted) fileInfoCollection.Add(new(jsonFile)); + return new(results); } - private static void WriteWithPartentsFile(IFileRead fileRead, string destinationDirectory, List fileInfoCollection, ReadOnlyCollection records, ReadOnlyCollection workItemTypes, string fileName) + private static ReadOnlyCollection WriteWithPartentsFile(IFileRead fileRead, string destinationDirectory, List fileInfoCollection, ReadOnlyCollection records, ReadOnlyCollection workItemTypes, string fileName) { + List results = new(); List filtered = new(); Record record; foreach (Record r in records) @@ -213,13 +237,14 @@ public class ProcessData : IProcessData record = new(r.WorkItem, r.Parent, Array.Empty(), Array.Empty(), Array.Empty()); filtered.Add(record); } - string json = JsonSerializer.Serialize(filtered, new JsonSerializerOptions() { WriteIndented = true }); + string? json = GetJson(filtered, results); string jsonFile = Path.Combine(destinationDirectory, $"{fileName}.json"); string jsonOld = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile); - if (json != jsonOld) + if (!string.IsNullOrEmpty(json) && json != jsonOld) File.WriteAllText(jsonFile, json); if (!fileRead.IsEAFHosted) fileInfoCollection.Add(new(jsonFile)); + return new(results); } private static void AppendLines(string url, List spaces, List lines, ReadOnlyCollection records, string workItemType) @@ -304,9 +329,9 @@ public class ProcessData : IProcessData try { childRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Child", nests, keepRelations); // Forward - // records = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse relatedRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Related", nests, keepRelations); // Related successorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Successor", nests, keepRelations); // Forward + // predecessorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse record = Record.Get(keyValuePair.Value, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations); } catch (Exception) diff --git a/Adaptation/FileHandlers/Priority/Aggregation.cs b/Adaptation/FileHandlers/Priority/Aggregation.cs index 6f1c885..bcd89e0 100644 --- a/Adaptation/FileHandlers/Priority/Aggregation.cs +++ b/Adaptation/FileHandlers/Priority/Aggregation.cs @@ -40,4 +40,10 @@ public class Aggregation [JsonSerializable(typeof(Aggregation))] internal partial class AggregationSourceGenerationContext : JsonSerializerContext { +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Aggregation[]))] +internal partial class AggregationCollectionSourceGenerationContext : JsonSerializerContext +{ } \ No newline at end of file diff --git a/Adaptation/FileHandlers/Priority/FileRead.cs b/Adaptation/FileHandlers/Priority/FileRead.cs index 8f149bf..9a3da2e 100644 --- a/Adaptation/FileHandlers/Priority/FileRead.cs +++ b/Adaptation/FileHandlers/Priority/FileRead.cs @@ -262,7 +262,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } try @@ -277,7 +280,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } } diff --git a/Adaptation/FileHandlers/json/FileRead.cs b/Adaptation/FileHandlers/json/FileRead.cs index 0bc7791..2692cbe 100644 --- a/Adaptation/FileHandlers/json/FileRead.cs +++ b/Adaptation/FileHandlers/json/FileRead.cs @@ -11,7 +11,9 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Text.Json; +using System.Text.RegularExpressions; using System.Threading; namespace Adaptation.FileHandlers.json; @@ -111,14 +113,54 @@ public class FileRead : Shared.FileRead, IFileRead #nullable enable - private static ReadOnlyCollection GetWorkItems(ReadOnlyCollection valueWithReqCollection) + private static string GetParamCase(string value) { - List results = new(); + string result; + StringBuilder stringBuilder = new(value); + List matches = new(); + MatchCollection matchCollection = Regex.Matches(value, "([A-Z]+(.))"); + foreach (object? item in matchCollection) + { + if (item is not Match match) + continue; + matches.Add(match); + } + for (int i = matches.Count - 1; i > -1; i--) + _ = stringBuilder.Insert(matches[i].Index, '-'); + string[] segments = Regex.Split(stringBuilder.ToString().ToLower(), "[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+"); + result = string.Join("-", segments).Trim('-'); + return result; + } + + private static ReadOnlyDictionary GetKeyValuePairs(ReadOnlyCollection valueWithReqCollection) + { + Dictionary results = new(); Fields fields; WorkItem workItem; + Relation relation; + string[] segments; + List relations; + WorkItems.Attribute attributes; foreach (Value value in valueWithReqCollection) { + relations = new(); fields = value.Fields; + if (value.Relations is not null) + { + foreach (WIQL.Relation r in value.Relations) + { + segments = r?.Attributes is null ? Array.Empty() : r.URL.Split('/'); + if (segments.Length < 2) + continue; + if (r?.Attributes is null) + continue; + if (!int.TryParse(segments[segments.Length - 1], out int id)) + continue; + attributes = new(r.Attributes.IsLocked, r.Attributes.Name, null, null, null); + relation = new(attributes, id, r.Rel); + relations.Add(relation); + } + } workItem = new(fields.MicrosoftVSTSCommonActivatedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonActivatedDate, fields.SystemAreaPath, fields.SystemAssignedTo?.DisplayName, @@ -133,7 +175,7 @@ public class FileRead : Shared.FileRead, IFileRead fields.SystemIterationPath, fields.SystemParent == 0 ? null : fields.SystemParent, fields.MicrosoftVSTSCommonPriority == 0 ? null : fields.MicrosoftVSTSCommonPriority, - value.Relations, + relations.ToArray(), fields.CustomRequester?.DisplayName, fields.MicrosoftVSTSCommonResolvedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonResolvedDate, value.Rev, @@ -143,10 +185,41 @@ public class FileRead : Shared.FileRead, IFileRead fields.SystemTags, fields.MicrosoftVSTSSchedulingTargetDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingTargetDate, fields.MicrosoftVSTSCommonTimeCriticality is null or 0 ? null : (long)fields.MicrosoftVSTSCommonTimeCriticality, - fields.SystemTitle, + fields.SystemTitle.Trim(), null, fields.CustomWSJF is null or 0 ? null : (long)fields.CustomWSJF, fields.SystemWorkItemType); + results.Add(workItem.Id, workItem); + } + return new(results); + } + + private static ReadOnlyCollection GetWorkItems(ReadOnlyCollection valueWithReqCollection) + { + List results = new(); + WorkItem workItem; + Relation relation; + string parameterTitle; + List relations; + WorkItem? relationWorkItem; + WorkItems.Attribute attributes; + ReadOnlyDictionary keyValuePairs = GetKeyValuePairs(valueWithReqCollection); + foreach (KeyValuePair keyValuePair in keyValuePairs) + { + relations = new(); + if (keyValuePair.Value.Relations is not null) + { + foreach (Relation r in keyValuePair.Value.Relations) + { + if (!keyValuePairs.TryGetValue(r.Id, out relationWorkItem)) + continue; + parameterTitle = GetParamCase(relationWorkItem.Title); + attributes = new(r.Attributes.IsLocked, r.Attributes.Name, parameterTitle, relationWorkItem.State, relationWorkItem.WorkItemType); + relation = new(attributes, r.Id, r.Rel); + relations.Add(relation); + } + } + workItem = WorkItem.Get(keyValuePair.Value, relations.ToArray()); results.Add(workItem); } return new(results); @@ -237,7 +310,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } try @@ -252,7 +328,10 @@ public class FileRead : Shared.FileRead, IFileRead string subject = string.Concat("Exception:", _CellInstanceConnectionName); string body = string.Concat(exception.Message, Environment.NewLine, Environment.NewLine, exception.StackTrace); try - { _SMTP.SendHighPriorityEmailMessage(subject, body); } + { + _SMTP.SendHighPriorityEmailMessage(subject, body); + File.WriteAllText(".email", body); + } catch (Exception) { } } } diff --git a/Adaptation/FileHandlers/json/WIQL/Attribute.cs b/Adaptation/FileHandlers/json/WIQL/Attribute.cs new file mode 100644 index 0000000..d800eda --- /dev/null +++ b/Adaptation/FileHandlers/json/WIQL/Attribute.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace Adaptation.FileHandlers.json.WIQL; + +internal class Attribute +{ + + [JsonConstructor] + public Attribute(bool isLocked, + string name) + { + IsLocked = isLocked; + Name = name; + } + + [JsonPropertyName("isLocked")] public bool IsLocked { get; } + [JsonPropertyName("name")] public string Name { get; } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Attribute))] +internal partial class WIQLAttributeSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Adaptation/FileHandlers/json/WIQL/Relation.cs b/Adaptation/FileHandlers/json/WIQL/Relation.cs new file mode 100644 index 0000000..7db0892 --- /dev/null +++ b/Adaptation/FileHandlers/json/WIQL/Relation.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; + +namespace Adaptation.FileHandlers.json.WIQL; + +internal class Relation +{ + + [JsonConstructor] + public Relation(string rel, + string url, + Attribute attributes) + { + Rel = rel; + Rel = rel; + URL = url; + Attributes = attributes; + } + + [JsonPropertyName("attributes")] public Attribute Attributes { get; set; } + [JsonPropertyName("rel")] public string Rel { get; set; } + [JsonPropertyName("url")] public string URL { get; set; } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Relation))] +internal partial class WIQLRelationSourceGenerationContext : JsonSerializerContext +{ +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Relation[]))] +internal partial class WIQLRelationCollectionSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Adaptation/FileHandlers/json/WorkItems/Attribute.cs b/Adaptation/FileHandlers/json/WorkItems/Attribute.cs index 63b61f9..84cef4e 100644 --- a/Adaptation/FileHandlers/json/WorkItems/Attribute.cs +++ b/Adaptation/FileHandlers/json/WorkItems/Attribute.cs @@ -5,16 +5,27 @@ namespace Adaptation.FileHandlers.json.WorkItems; internal class Attribute { +#nullable enable + [JsonConstructor] public Attribute(bool isLocked, - string name) + string name, + string? parameterTitle, + string? state, + string? workItemType) { IsLocked = isLocked; Name = name; + ParameterTitle = parameterTitle; + State = state; + WorkItemType = workItemType; } [JsonPropertyName("isLocked")] public bool IsLocked { get; } [JsonPropertyName("name")] public string Name { get; } + [JsonPropertyName("parameterTitle")] public string? ParameterTitle { get; set; } + [JsonPropertyName("state")] public string? State { get; set; } + [JsonPropertyName("workItemType")] public string? WorkItemType { get; set; } } diff --git a/Adaptation/FileHandlers/json/WorkItems/Record.cs b/Adaptation/FileHandlers/json/WorkItems/Record.cs index 821506f..d7a9ba6 100644 --- a/Adaptation/FileHandlers/json/WorkItems/Record.cs +++ b/Adaptation/FileHandlers/json/WorkItems/Record.cs @@ -88,30 +88,13 @@ internal class Record return result; } - private static int? GetIdFromUrl(string relationName, Relation relation) - { - int? result; - string[] segments = relation?.Attributes is null || relation.Attributes.Name != relationName ? Array.Empty() : relation.URL.Split('/'); - if (segments.Length < 2) - result = null; - else - { - if (!int.TryParse(segments[segments.Length - 1], out int id)) - result = null; - else - result = id; - } - return result; - } - internal static ReadOnlyCollection GetKeyValuePairs(ReadOnlyDictionary keyValuePairs, WorkItem workItem, string relationName, List nests, bool keepRelations) { List results = new(); - int? childId; Record record; nests.Add(true); - WorkItem? childWorkItem; WorkItem? parentWorkItem; + WorkItem? relationWorkItem; List collection = new(); ReadOnlyCollection childRecords; ReadOnlyCollection relatedRecords; @@ -121,12 +104,13 @@ internal class Record collection.Clear(); foreach (Relation relation in workItem.Relations) { - childId = GetIdFromUrl(relationName, relation); - if (childId is not null && workItem.Parent is not null && relation?.URL is not null && relation.URL.Contains(workItem.Parent.Value.ToString())) + if (relation.Attributes.Name != relationName) continue; - if (childId is null || !keyValuePairs.TryGetValue(childId.Value, out childWorkItem)) + if (workItem.Parent is not null && relation.Id == workItem.Parent.Value) continue; - collection.Add(childWorkItem); + if (!keyValuePairs.TryGetValue(relation.Id, out relationWorkItem)) + continue; + collection.Add(relationWorkItem); } collection = (from l in collection orderby l.State != "Closed", l.Id select l).ToList(); foreach (WorkItem w in collection) @@ -138,14 +122,9 @@ internal class Record else _ = keyValuePairs.TryGetValue(w.Parent.Value, out parentWorkItem); childRecords = GetKeyValuePairs(keyValuePairs, w, "Child", nests, keepRelations); // Forward - // records = GetKeyValuePairs(keyValuePairs, w, "Predecessor", nests); // Reverse // successorRecords = GetKeyValuePairs(keyValuePairs, w, "Successor", nests); // Forward - // if (successorRecords.Count > 0) - // { - // if (successorRecords.Count > 0) - // { } - // } successorRecords = new(Array.Empty()); + // predecessorRecords = GetKeyValuePairs(keyValuePairs, w, "Predecessor", nests); // Reverse relatedRecords = GetKeyValuePairs(keyValuePairs, w, "Related", nests, keepRelations); // Related record = Get(w, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations); results.Add(record); @@ -160,4 +139,10 @@ internal class Record [JsonSerializable(typeof(Record))] internal partial class RecordSourceGenerationContext : JsonSerializerContext { +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Record[]))] +internal partial class RecordCollectionSourceGenerationContext : JsonSerializerContext +{ } \ No newline at end of file diff --git a/Adaptation/FileHandlers/json/WorkItems/Relation.cs b/Adaptation/FileHandlers/json/WorkItems/Relation.cs index 53c1282..00e4b31 100644 --- a/Adaptation/FileHandlers/json/WorkItems/Relation.cs +++ b/Adaptation/FileHandlers/json/WorkItems/Relation.cs @@ -6,18 +6,18 @@ internal class Relation { [JsonConstructor] - public Relation(string rel, - string url, - Attribute attributes) + public Relation(Attribute attributes, + int id, + string rel) { - Rel = rel; - URL = url; Attributes = attributes; + Id = id; + Rel = rel; } [JsonPropertyName("attributes")] public Attribute Attributes { get; set; } + [JsonPropertyName("id")] public int Id { get; set; } [JsonPropertyName("rel")] public string Rel { get; set; } - [JsonPropertyName("url")] public string URL { get; set; } } diff --git a/Adaptation/FileHandlers/json/WorkItems/Value.cs b/Adaptation/FileHandlers/json/WorkItems/Value.cs index d158b67..84be3d1 100644 --- a/Adaptation/FileHandlers/json/WorkItems/Value.cs +++ b/Adaptation/FileHandlers/json/WorkItems/Value.cs @@ -10,7 +10,7 @@ internal class Value int id, int rev, Fields fields, - Relation[] relations, + WIQL.Relation[] relations, CommentVersionRef commentVersionRef, string url ) @@ -26,7 +26,7 @@ internal class Value [JsonPropertyName("commentVersionRef")] public CommentVersionRef CommentVersionRef { get; } [JsonPropertyName("fields")] public Fields Fields { get; } [JsonPropertyName("id")] public int Id { get; } - [JsonPropertyName("relations")] public Relation[] Relations { get; } + [JsonPropertyName("relations")] public WIQL.Relation[] Relations { get; } [JsonPropertyName("rev")] public int Rev { get; } [JsonPropertyName("url")] public string Url { get; } diff --git a/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs b/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs index c29f0da..0738560 100644 --- a/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs +++ b/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs @@ -70,6 +70,39 @@ internal class WorkItem public override string ToString() => $"{Id} - {WorkItemType} - {Title}"; + public static WorkItem Get(WorkItem workItem, Relation[] relations) + { + WorkItem result = new(workItem.ActivatedDate, + workItem.AreaPath, + workItem.AssignedTo, + workItem.BusinessValue, + workItem.ChangedDate, + workItem.ClosedDate, + workItem.CommentCount, + workItem.CreatedDate, + workItem.Description, + workItem.Effort, + workItem.Id, + workItem.IterationPath, + workItem.Parent, + workItem.Priority, + relations, + workItem.Requester, + workItem.ResolvedDate, + workItem.Revision, + workItem.RiskReductionMinusOpportunityEnablement, + workItem.StartDate, + workItem.State, + workItem.Tags, + workItem.TargetDate, + workItem.TimeCriticality, + workItem.Title, + workItem.Violation, + workItem.WeightedShortestJobFirst, + workItem.WorkItemType); + return result; + } + public static WorkItem? GetWithOutRelations(WorkItem? workItem) { WorkItem? result = workItem is null ? null : new(workItem.ActivatedDate, diff --git a/Adaptation/_Tests/Extract/Development/v2.57.0/BACKLOG.cs b/Adaptation/_Tests/Extract/Development/v2.57.0/BACKLOG.cs index 217d2c8..906e29d 100644 --- a/Adaptation/_Tests/Extract/Development/v2.57.0/BACKLOG.cs +++ b/Adaptation/_Tests/Extract/Development/v2.57.0/BACKLOG.cs @@ -48,8 +48,8 @@ public class BACKLOG { string check = "*.json"; bool validatePDSF = false; - MethodBase methodBase = new StackFrame().GetMethod(); _BACKLOG.Development__v2_57_0__BACKLOG__json(); + MethodBase methodBase = new StackFrame().GetMethod(); Assert.IsFalse(string.IsNullOrEmpty(_BACKLOG.AdaptationTesting.TestContext.FullyQualifiedTestClassName)); string[] variables = _BACKLOG.AdaptationTesting.GetVariables(methodBase, check, validatePDSF); IFileRead fileRead = _BACKLOG.AdaptationTesting.Get(methodBase, sourceFileLocation: variables[2], sourceFileFilter: variables[3], useCyclicalForDescription: false); diff --git a/MESAFIBACKLOG.csproj b/MESAFIBACKLOG.csproj index e18b9e3..f2b633a 100644 --- a/MESAFIBACKLOG.csproj +++ b/MESAFIBACKLOG.csproj @@ -119,8 +119,10 @@ + +