From d9243def307bcdb8b592baf8a7c3bfd97f86063a Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 22 Sep 2025 14:22:57 -0600 Subject: [PATCH 1/5] feat: Adds ability to read configs generated from v2 config tool that are pre-merged don't have system or template objects Refactor config handling and improve documentation - Updated `PortalConfigReader.cs` to use constants for configuration keys, enhancing maintainability and readability. Improved error logging with `Debug.LogError`. - Modified `ConfigReader.cs` to handle v2 configuration format, streamlining the loading process and avoiding redundant parsing. - Added XML documentation comments to properties in `EssentialsConfig.cs`, improving code documentation. Initialized `Rooms` property in the constructor. - Enhanced `SystemTemplateConfigs` class with XML documentation for better clarity on its properties. --- .../Config/PortalConfigReader.cs | 111 ++++++++++-------- .../Config/Essentials/ConfigReader.cs | 31 +++-- .../Config/Essentials/EssentialsConfig.cs | 33 ++++-- 3 files changed, 112 insertions(+), 63 deletions(-) diff --git a/src/PepperDash.Core/Config/PortalConfigReader.cs b/src/PepperDash.Core/Config/PortalConfigReader.cs index ffb2d16b..b048bdc4 100644 --- a/src/PepperDash.Core/Config/PortalConfigReader.cs +++ b/src/PepperDash.Core/Config/PortalConfigReader.cs @@ -9,40 +9,59 @@ using Serilog.Events; namespace PepperDash.Core.Config { + + /// /// Reads a Portal formatted config file /// public class PortalConfigReader { - /// - /// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object. - /// - /// JObject of config file - public static void ReadAndMergeFileIfNecessary(string filePath, string savePath) + const string template = "template"; + const string system = "system"; + const string systemUrl = "system_url"; + const string templateUrl = "template_url"; + const string info = "info"; + const string devices = "devices"; + const string rooms = "rooms"; + const string sourceLists = "sourceLists"; + const string destinationLists = "destinationLists"; + const string cameraLists = "cameraLists"; + const string audioControlPointLists = "audioControlPointLists"; + + const string tieLines = "tieLines"; + const string joinMaps = "joinMaps"; + const string global = "global"; + + + /// + /// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object. + /// + /// JObject of config file + public static void ReadAndMergeFileIfNecessary(string filePath, string savePath) { try { if (!File.Exists(filePath)) { - Debug.Console(1, Debug.ErrorLogLevel.Error, + Debug.LogError( "ERROR: Configuration file not present. Please load file to {0} and reset program", filePath); } using (StreamReader fs = new StreamReader(filePath)) { var jsonObj = JObject.Parse(fs.ReadToEnd()); - if(jsonObj["template"] != null && jsonObj["system"] != null) + if(jsonObj[template] != null && jsonObj[system] != null) { // it's a double-config, merge it. var merged = MergeConfigs(jsonObj); - if (jsonObj["system_url"] != null) + if (jsonObj[systemUrl] != null) { - merged["systemUrl"] = jsonObj["system_url"].Value(); + merged[systemUrl] = jsonObj[systemUrl].Value(); } - if (jsonObj["template_url"] != null) + if (jsonObj[templateUrl] != null) { - merged["templateUrl"] = jsonObj["template_url"].Value(); + merged[templateUrl] = jsonObj[templateUrl].Value(); } jsonObj = merged; @@ -77,62 +96,62 @@ namespace PepperDash.Core.Config var merged = new JObject(); // Put together top-level objects - if (system["info"] != null) - merged.Add("info", Merge(template["info"], system["info"], "infO")); + if (system[info] != null) + merged.Add(info, Merge(template[info], system[info], info)); else - merged.Add("info", template["info"]); + merged.Add(info, template[info]); - merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray, - system["devices"] as JArray, "key", "devices")); + merged.Add(devices, MergeArraysOnTopLevelProperty(template[devices] as JArray, + system[devices] as JArray, "key", devices)); - if (system["rooms"] == null) - merged.Add("rooms", template["rooms"]); + if (system[rooms] == null) + merged.Add(rooms, template[rooms]); else - merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray, - system["rooms"] as JArray, "key", "rooms")); + merged.Add(rooms, MergeArraysOnTopLevelProperty(template[rooms] as JArray, + system[rooms] as JArray, "key", rooms)); - if (system["sourceLists"] == null) - merged.Add("sourceLists", template["sourceLists"]); + if (system[sourceLists] == null) + merged.Add(sourceLists, template[sourceLists]); else - merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists")); + merged.Add(sourceLists, Merge(template[sourceLists], system[sourceLists], sourceLists)); - if (system["destinationLists"] == null) - merged.Add("destinationLists", template["destinationLists"]); + if (system[destinationLists] == null) + merged.Add(destinationLists, template[destinationLists]); else - merged.Add("destinationLists", - Merge(template["destinationLists"], system["destinationLists"], "destinationLists")); + merged.Add(destinationLists, + Merge(template[destinationLists], system[destinationLists], destinationLists)); - if (system["cameraLists"] == null) - merged.Add("cameraLists", template["cameraLists"]); + if (system[cameraLists] == null) + merged.Add(cameraLists, template[cameraLists]); else - merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists")); + merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists)); - if (system["audioControlPointLists"] == null) - merged.Add("audioControlPointLists", template["audioControlPointLists"]); + if (system[audioControlPointLists] == null) + merged.Add(audioControlPointLists, template[audioControlPointLists]); else - merged.Add("audioControlPointLists", - Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists")); + merged.Add(audioControlPointLists, + Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists)); // Template tie lines take precedence. Config tool doesn't do them at system // level anyway... - if (template["tieLines"] != null) - merged.Add("tieLines", template["tieLines"]); - else if (system["tieLines"] != null) - merged.Add("tieLines", system["tieLines"]); + if (template[tieLines] != null) + merged.Add(tieLines, template[tieLines]); + else if (system[tieLines] != null) + merged.Add(tieLines, system[tieLines]); else - merged.Add("tieLines", new JArray()); + merged.Add(tieLines, new JArray()); - if (template["joinMaps"] != null) - merged.Add("joinMaps", template["joinMaps"]); + if (template[joinMaps] != null) + merged.Add(joinMaps, template[joinMaps]); else - merged.Add("joinMaps", new JObject()); + merged.Add(joinMaps, new JObject()); - if (system["global"] != null) - merged.Add("global", Merge(template["global"], system["global"], "global")); + if (system[global] != null) + merged.Add(global, Merge(template[global], system[global], global)); else - merged.Add("global", template["global"]); + merged.Add(global, template[global]); //Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged); return merged; @@ -228,7 +247,7 @@ namespace PepperDash.Core.Config } catch (Exception e) { - Debug.Console(1, Debug.ErrorLogLevel.Warning, "Cannot merge items at path {0}: \r{1}", propPath, e); + Debug.LogError("Cannot merge items at path {propPath}: \r{e}", propPath, e); } } } diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs index b93de1c4..ea5d2672 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs @@ -124,22 +124,35 @@ namespace PepperDash.Essentials.Core.Config Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config"); return true; - } + } else { - var doubleObj = JObject.Parse(fs.ReadToEnd()); - ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject(); + var parsedConfig = JObject.Parse(fs.ReadToEnd()); - // Extract SystemUrl and TemplateUrl into final config output - - if (doubleObj["system_url"] != null) + // Check if it's a v2 config (missing "system" or "template" nodes) + // this means it's already merged by the Portal API + // from the v2 config tool + var isV2Config = parsedConfig["system"] == null || parsedConfig["template"] == null; + + if (isV2Config) { - ConfigObject.SystemUrl = doubleObj["system_url"].Value(); + Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary."); + ConfigObject = parsedConfig.ToObject(); + Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config"); + return true; } - if (doubleObj["template_url"] != null) + // Extract SystemUrl and TemplateUrl into final config output + ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject(); + + if (parsedConfig["system_url"] != null) { - ConfigObject.TemplateUrl = doubleObj["template_url"].Value(); + ConfigObject.SystemUrl = parsedConfig["system_url"].Value(); + } + + if (parsedConfig["template_url"] != null) + { + ConfigObject.TemplateUrl = parsedConfig["template_url"].Value(); } } diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs index 630ffbdf..e1a46b54 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs @@ -16,13 +16,21 @@ namespace PepperDash.Essentials.Core.Config /// public class EssentialsConfig : BasicConfig { - [JsonProperty("system_url")] + /// + /// Gets or sets the SystemUrl + /// + [JsonProperty("system_url")] public string SystemUrl { get; set; } - [JsonProperty("template_url")] + /// + /// Gets or sets the TemplateUrl + /// + [JsonProperty("template_url")] public string TemplateUrl { get; set; } - + /// + /// Gets the SystemUuid extracted from the SystemUrl + /// [JsonProperty("systemUuid")] public string SystemUuid { @@ -45,6 +53,9 @@ namespace PepperDash.Essentials.Core.Config } } + /// + /// Gets the TemplateUuid extracted from the TemplateUrl + /// [JsonProperty("templateUuid")] public string TemplateUuid { @@ -67,13 +78,16 @@ namespace PepperDash.Essentials.Core.Config } } - [JsonProperty("rooms")] /// /// Gets or sets the Rooms /// + [JsonProperty("rooms")] + public List Rooms { get; set; } - + /// + /// Initializes a new instance of the class. + /// public EssentialsConfig() : base() { @@ -86,11 +100,14 @@ namespace PepperDash.Essentials.Core.Config /// public class SystemTemplateConfigs { - /// - /// Gets or sets the System - /// + /// + /// Gets or sets the System + /// public EssentialsConfig System { get; set; } + /// + /// Gets or sets the Template + /// public EssentialsConfig Template { get; set; } } } \ No newline at end of file From ff46fb8f29f69dc3953d701184c4d7e4b2750a54 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 22 Sep 2025 14:55:58 -0600 Subject: [PATCH 2/5] feat: Add versioning support to EssentialsConfig Introduce `Versions` property in `EssentialsConfig` to hold version information. Add `VersionData` class for Essentials and package versions, and `NugetVersion` class for individual package details. Retain and document `SystemTemplateConfigs` class. --- .../Config/Essentials/EssentialsConfig.cs | 59 +++++++++++++++++-- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs index e1a46b54..9665938f 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs @@ -85,6 +85,11 @@ namespace PepperDash.Essentials.Core.Config public List Rooms { get; set; } + /// + /// Gets or sets the Versions + /// + public VersionData Versions { get; set; } + /// /// Initializes a new instance of the class. /// @@ -94,11 +99,55 @@ namespace PepperDash.Essentials.Core.Config Rooms = new List(); } } - - /// - /// Represents a SystemTemplateConfigs - /// - public class SystemTemplateConfigs + + /// + /// Represents version data for Essentials and its packages + /// + public class VersionData + { + /// + /// Gets or sets the Essentials version + /// + [JsonProperty("essentials")] + public NugetVersion Essentials { get; set; } + + /// + /// Gets or sets the list of Packages + /// + [JsonProperty("packages")] + public List Packages { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public VersionData() + { + Packages = new List(); + } + } + + /// + /// Represents a NugetVersion + /// + public class NugetVersion + { + /// + /// Gets or sets the Version + /// + [JsonProperty("version")] + public string Version { get; set; } + + /// + /// Gets or sets the PackageId + /// + [JsonProperty("packageId")] + public string PackageId { get; set; } + } + + /// + /// Represents a SystemTemplateConfigs + /// + public class SystemTemplateConfigs { /// /// Gets or sets the System From 9de94bd65ff5733464cc190f047087198af14cbd Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 22 Sep 2025 15:05:06 -0600 Subject: [PATCH 3/5] fix: Update v2 config detection criteria Changed the logic for identifying v2 configuration files. The check now looks for the presence of a "versions" node instead of the absence of "system" or "template" nodes, reflecting an update in the configuration file structure. --- .../Config/Essentials/ConfigReader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs index ea5d2672..2591e8aa 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/ConfigReader.cs @@ -129,10 +129,10 @@ namespace PepperDash.Essentials.Core.Config { var parsedConfig = JObject.Parse(fs.ReadToEnd()); - // Check if it's a v2 config (missing "system" or "template" nodes) + // Check if it's a v2 config (check for "version" node) // this means it's already merged by the Portal API // from the v2 config tool - var isV2Config = parsedConfig["system"] == null || parsedConfig["template"] == null; + var isV2Config = parsedConfig["versions"] != null; if (isV2Config) { From 5409db193c5d66451eec759773f7463a7042f605 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Oct 2025 13:53:23 -0600 Subject: [PATCH 4/5] Update src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Config/Essentials/EssentialsConfig.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs index 9665938f..7108762b 100644 --- a/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs +++ b/src/PepperDash.Essentials.Core/Config/Essentials/EssentialsConfig.cs @@ -82,7 +82,6 @@ namespace PepperDash.Essentials.Core.Config /// Gets or sets the Rooms /// [JsonProperty("rooms")] - public List Rooms { get; set; } /// From 10399a1be898b3eb267c525d663fdbb928aaa4eb Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Oct 2025 13:54:08 -0600 Subject: [PATCH 5/5] Update src/PepperDash.Core/Config/PortalConfigReader.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Core/Config/PortalConfigReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Core/Config/PortalConfigReader.cs b/src/PepperDash.Core/Config/PortalConfigReader.cs index b048bdc4..41ca2cbd 100644 --- a/src/PepperDash.Core/Config/PortalConfigReader.cs +++ b/src/PepperDash.Core/Config/PortalConfigReader.cs @@ -247,7 +247,7 @@ namespace PepperDash.Core.Config } catch (Exception e) { - Debug.LogError("Cannot merge items at path {propPath}: \r{e}", propPath, e); + Debug.LogError($"Cannot merge items at path {propPath}: \r{e}"); } } }