Refactor JSON deserialization in ProcessData classes and improve error handling

This commit is contained in:
2025-09-24 12:53:49 -07:00
parent 68c3a8b5f3
commit 5f581ec1b5
7 changed files with 90 additions and 56 deletions

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -44,12 +45,11 @@ public class ProcessData : IProcessData
{ {
List<Description> results = new(); List<Description> results = new();
Description? description; Description? description;
JsonSerializerOptions jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString };
foreach (JsonElement jsonElement in jsonElements) foreach (JsonElement jsonElement in jsonElements)
{ {
if (jsonElement.ValueKind != JsonValueKind.Object) if (jsonElement.ValueKind != JsonValueKind.Object)
throw new Exception(); throw new Exception();
description = JsonSerializer.Deserialize<Description>(jsonElement.ToString(), jsonSerializerOptions); description = JsonSerializer.Deserialize(jsonElement.ToString(), DescriptionSourceGenerationContext.Default.Description);
if (description is null) if (description is null)
continue; continue;
results.Add(description); results.Add(description);

View File

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -221,12 +222,11 @@ public class ProcessData : IProcessData
{ {
List<Description> results = new(); List<Description> results = new();
Description? description; Description? description;
JsonSerializerOptions jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString };
foreach (JsonElement jsonElement in jsonElements) foreach (JsonElement jsonElement in jsonElements)
{ {
if (jsonElement.ValueKind != JsonValueKind.Object) if (jsonElement.ValueKind != JsonValueKind.Object)
throw new Exception(); throw new Exception();
description = JsonSerializer.Deserialize<Description>(jsonElement.ToString(), jsonSerializerOptions); description = JsonSerializer.Deserialize(jsonElement.ToString(), DescriptionSourceGenerationContext.Default.Description);
if (description is null) if (description is null)
continue; continue;
results.Add(description); results.Add(description);

View File

@ -399,12 +399,11 @@ public class ProcessData : IProcessData
{ {
List<Description> results = new(); List<Description> results = new();
Description? description; Description? description;
JsonSerializerOptions jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString };
foreach (JsonElement jsonElement in jsonElements) foreach (JsonElement jsonElement in jsonElements)
{ {
if (jsonElement.ValueKind != JsonValueKind.Object) if (jsonElement.ValueKind != JsonValueKind.Object)
throw new Exception(); throw new Exception();
description = JsonSerializer.Deserialize<Description>(jsonElement.ToString(), jsonSerializerOptions); description = JsonSerializer.Deserialize(jsonElement.ToString(), DescriptionSourceGenerationContext.Default.Description);
if (description is null) if (description is null)
continue; continue;
results.Add(description); results.Add(description);

View File

@ -615,12 +615,11 @@ public class ProcessData : IProcessData
{ {
List<Description> results = new(); List<Description> results = new();
Description? description; Description? description;
JsonSerializerOptions jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString };
foreach (JsonElement jsonElement in jsonElements) foreach (JsonElement jsonElement in jsonElements)
{ {
if (jsonElement.ValueKind != JsonValueKind.Object) if (jsonElement.ValueKind != JsonValueKind.Object)
throw new Exception(); throw new Exception();
description = JsonSerializer.Deserialize<Description>(jsonElement.ToString(), jsonSerializerOptions); description = JsonSerializer.Deserialize(jsonElement.ToString(), DescriptionSourceGenerationContext.Default.Description);
if (description is null) if (description is null)
continue; continue;
results.Add(description); results.Add(description);

View File

@ -143,7 +143,7 @@ function updateRecordCoD(b, r, t, c, e, w, workItem, highestTotalStoryPoints, da
let businessValue = workItem.BusinessValue; let businessValue = workItem.BusinessValue;
let timeCriticality = workItem.TimeCriticality; let timeCriticality = workItem.TimeCriticality;
let riskReductionMinusOpportunityEnablement = workItem.RiskReductionMinusOpportunityEnablement; let riskReductionMinusOpportunityEnablement = workItem.RiskReductionMinusOpportunityEnablement;
let weightedShortestJobFirst = workItem.WeightedShortestJobFirst == undefined ? null : workItem.WeightedShortestJobFirst.toFixed(2); let weightedShortestJobFirst = workItem.WeightedShortestJobFirst == undefined ? '&nbsp;' : workItem.WeightedShortestJobFirst.toFixed(2);
workItem.CumulativeStoryPoints = '&nbsp;'; workItem.CumulativeStoryPoints = '&nbsp;';
workItem.TotalStoryPoints = totalStoryPoints + ' User Story Point(s)'; workItem.TotalStoryPoints = totalStoryPoints + ' User Story Point(s)';
workItem.AbsoluteDelta = data.Effort == undefined || data.Effort.FibonacciAverage == undefined || totalStoryPoints == undefined || totalStoryPoints === 0 ? '&nbsp;' : round(Math.abs(data.Effort.FibonacciAverage - ((totalStoryPoints / highestTotalStoryPoints) * 5)), 1); workItem.AbsoluteDelta = data.Effort == undefined || data.Effort.FibonacciAverage == undefined || totalStoryPoints == undefined || totalStoryPoints === 0 ? '&nbsp;' : round(Math.abs(data.Effort.FibonacciAverage - ((totalStoryPoints / highestTotalStoryPoints) * 5)), 1);
@ -159,15 +159,19 @@ function updateRecordCoD(b, r, t, c, e, w, workItem, highestTotalStoryPoints, da
workItem.TimeCriticalityNotifications = data.TimeCriticality == undefined ? '&nbsp;' : getNotifications(t, data.TimeCriticality); workItem.TimeCriticalityNotifications = data.TimeCriticality == undefined ? '&nbsp;' : getNotifications(t, data.TimeCriticality);
workItem.RiskReductionMinusOpportunityEnablementNotifications = data.RiskReductionOpportunityEnablement == undefined ? '&nbsp;' : getNotifications(r, data.RiskReductionOpportunityEnablement); workItem.RiskReductionMinusOpportunityEnablementNotifications = data.RiskReductionOpportunityEnablement == undefined ? '&nbsp;' : getNotifications(r, data.RiskReductionOpportunityEnablement);
workItem.SortOrder = data.SortOrder == undefined ? 0 : data.SortOrder; workItem.SortOrder = data.SortOrder == undefined ? 0 : data.SortOrder;
let check = effort == workItem.Effort if (effort == undefined || businessValue == undefined || timeCriticality == undefined || riskReductionMinusOpportunityEnablement || weightedShortestJobFirst == undefined) {
&& businessValue === workItem.BusinessValue
&& timeCriticality === workItem.TimeCriticality
&& riskReductionMinusOpportunityEnablement === workItem.RiskReductionMinusOpportunityEnablement;
if (check && weightedShortestJobFirst == workItem.WeightedShortestJobFirst) {
workItem.api = ''; workItem.api = '';
} else { }
workItem.api = ` else {
### Work Item Patch ${check} WSJF ${workItem.Id} ${weightedShortestJobFirst} != ${workItem.WeightedShortestJobFirst} let check = effort === workItem.Effort
&& businessValue === workItem.BusinessValue
&& timeCriticality === workItem.TimeCriticality
&& riskReductionMinusOpportunityEnablement === workItem.RiskReductionMinusOpportunityEnablement;
if (check && weightedShortestJobFirst == workItem.WeightedShortestJobFirst) {
workItem.api = '';
} else {
workItem.api = `
### Work Item Patch ${check} WSJF ${workItem.Id} ${effort}:${workItem.Effort}; ${businessValue}:${workItem.BusinessValue}; ${timeCriticality}:${workItem.TimeCriticality}; ${riskReductionMinusOpportunityEnablement}:${workItem.RiskReductionMinusOpportunityEnablement}; ${weightedShortestJobFirst}:${workItem.WeightedShortestJobFirst};
patch {{Factory-Integration}}/_apis/wit/workitems/${workItem.Id}?api-version=7.0 patch {{Factory-Integration}}/_apis/wit/workitems/${workItem.Id}?api-version=7.0
Authorization: Basic {{PAT}} Authorization: Basic {{PAT}}
@ -178,44 +182,45 @@ Content-Type: application/json-patch+json
"op": "replace", "op": "replace",
"path": "/fields/Microsoft.VSTS.Common.BusinessValue", "path": "/fields/Microsoft.VSTS.Common.BusinessValue",
"value": "${workItem.BusinessValue}" "value": "${workItem.BusinessValue}"
}, },
{ {
"op": "replace", "op": "replace",
"path": "/fields/Microsoft.VSTS.Scheduling.Effort", "path": "/fields/Microsoft.VSTS.Scheduling.Effort",
"value": "${workItem.Effort}" "value": "${workItem.Effort}"
}, },
{ {
"op": "replace", "op": "replace",
"path": "/fields/Custom.RRminusOE", "path": "/fields/Custom.RRminusOE",
"value": "${workItem.RiskReductionMinusOpportunityEnablement}" "value": "${workItem.RiskReductionMinusOpportunityEnablement}"
}, },
{ {
"op": "replace", "op": "replace",
"path": "/fields/Microsoft.VSTS.Common.TimeCriticality", "path": "/fields/Microsoft.VSTS.Common.TimeCriticality",
"value": "${workItem.TimeCriticality}" "value": "${workItem.TimeCriticality}"
}, },
{ {
"op": "replace", "op": "replace",
"path": "/fields/Custom.WSJF", "path": "/fields/Custom.WSJF",
"value": "${workItem.WeightedShortestJobFirst}" "value": "${workItem.WeightedShortestJobFirst}"
}, },
{ {
"op": "replace", "op": "replace",
"path": "/fields/Custom.WSJFFib", "path": "/fields/Custom.WSJFFib",
"value": "${workItem.WeightedShortestJobFirst}" "value": "${workItem.WeightedShortestJobFirst}"
} }
] ]
HTTP 200 HTTP 200
[Asserts] [Asserts]
header "Content-Type" == "application/json; charset=utf-8; api-version=7.0" header "Content-Type" == "application/json; charset=utf-8; api-version=7.0"
jsonpath "$.id" == ${workItem.Id} jsonpath "$.id" == ${workItem.Id}
jsonpath "$.fields['Microsoft.VSTS.Common.BusinessValue']" == ${workItem.BusinessValue} jsonpath "$.fields['Microsoft.VSTS.Common.BusinessValue']" == ${workItem.BusinessValue}
jsonpath "$.fields['Microsoft.VSTS.Scheduling.Effort']" == ${workItem.Effort} jsonpath "$.fields['Microsoft.VSTS.Scheduling.Effort']" == ${workItem.Effort}
jsonpath "$.fields['Custom.RRminusOE']" == ${workItem.RiskReductionMinusOpportunityEnablement} jsonpath "$.fields['Custom.RRminusOE']" == ${workItem.RiskReductionMinusOpportunityEnablement}
jsonpath "$.fields['Microsoft.VSTS.Common.TimeCriticality']" == ${workItem.TimeCriticality} jsonpath "$.fields['Microsoft.VSTS.Common.TimeCriticality']" == ${workItem.TimeCriticality}
jsonpath "$.fields['Custom.WSJF']" == ${workItem.WeightedShortestJobFirst} jsonpath "$.fields['Custom.WSJF']" == ${workItem.WeightedShortestJobFirst}
jsonpath "$.fields['Custom.WSJFFib']" == ${workItem.WeightedShortestJobFirst}`; jsonpath "$.fields['Custom.WSJFFib']" == ${workItem.WeightedShortestJobFirst}`;
}
} }
} }
} }

View File

@ -9,7 +9,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading; using System.Threading;
namespace Adaptation.Shared; namespace Adaptation.Shared;
@ -447,12 +446,13 @@ public class FileRead : Properties.IFileRead
{ {
List<Properties.IDescription> results = new(); List<Properties.IDescription> results = new();
Duplicator.Description description; Duplicator.Description description;
JsonSerializerOptions jsonSerializerOptions = new() { NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString };
foreach (JsonElement jsonElement in jsonElements) foreach (JsonElement jsonElement in jsonElements)
{ {
if (jsonElement.ValueKind != JsonValueKind.Object) if (jsonElement.ValueKind != JsonValueKind.Object)
throw new Exception(); throw new Exception();
description = JsonSerializer.Deserialize<Duplicator.Description>(jsonElement.ToString(), jsonSerializerOptions); description = JsonSerializer.Deserialize(jsonElement.ToString(), Duplicator.SharedDescriptionSourceGenerationContext.Default.Description);
if (description is null)
continue;
results.Add(description); results.Add(description);
} }
return results; return results;

View File

@ -654,6 +654,17 @@ internal class ProcessDataStandardFormat
return results; return results;
} }
internal static JsonElement[] GetArray(string reportFullPath, string[] lines, ProcessDataStandardFormat processDataStandardFormat)
{
JsonElement[] results;
string? json = GetRecordsJson(reportFullPath, lines);
if (string.IsNullOrEmpty(json))
results = GetArray(processDataStandardFormat);
else
results = JsonSerializer.Deserialize(json, JsonElementCollectionSourceGenerationContext.Default.JsonElementArray) ?? throw new Exception();
return results;
}
internal static string GetPDSFText(IFileRead fileRead, Logistics logistics, JsonElement[] jsonElements, string logisticsText) internal static string GetPDSFText(IFileRead fileRead, Logistics logistics, JsonElement[] jsonElements, string logisticsText)
{ {
string result; string result;
@ -903,7 +914,7 @@ internal class ProcessDataStandardFormat
} }
foreach (KeyValuePair<string, List<string>> keyValuePair in results) foreach (KeyValuePair<string, List<string>> keyValuePair in results)
{ {
if (body.Count < 3) if (body.Count < 2)
break; break;
if (keyValuePair.Value.Count != body.Count) if (keyValuePair.Value.Count != body.Count)
continue; continue;
@ -956,6 +967,26 @@ internal class ProcessDataStandardFormat
return result; return result;
} }
private static string? GetRecordsJson(string reportFullPath, string[] lines)
{
string? result;
bool foundRecords = false;
List<string> results = new();
lines ??= File.ReadAllLines(reportFullPath);
foreach (string line in lines)
{
if (line.StartsWith("\"Records\""))
foundRecords = true;
if (!foundRecords)
continue;
if (line == "],")
break;
results.Add(line);
}
result = results.Count == 0 ? null : $"{string.Join(Environment.NewLine, results.Skip(1))}{Environment.NewLine}]";
return result;
}
} }
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]