From 8366eb5d15986a14662402fcc7ca41ae9f1a2ef4 Mon Sep 17 00:00:00 2001 From: Yuriy Sountsov Date: Wed, 7 Aug 2019 11:30:50 -0400 Subject: [PATCH 1/5] Use field for metadata to prevent reloading every time a term is read --- KenticoInspector.Core/AbstractReport.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/KenticoInspector.Core/AbstractReport.cs b/KenticoInspector.Core/AbstractReport.cs index ed372a04..56c5cc68 100644 --- a/KenticoInspector.Core/AbstractReport.cs +++ b/KenticoInspector.Core/AbstractReport.cs @@ -7,6 +7,7 @@ namespace KenticoInspector.Core { public abstract class AbstractReport : IReport, IWithMetadata where T : new() { + private ReportMetadata metadata; protected readonly IReportMetadataService reportMetadataService; public AbstractReport(IReportMetadataService reportMetadataService) @@ -27,7 +28,13 @@ public static string GetCodename(Type reportType) public abstract IList Tags { get; } - public ReportMetadata Metadata => reportMetadataService.GetReportMetadata(Codename); + public ReportMetadata Metadata + { + get + { + return metadata ?? (metadata = reportMetadataService.GetReportMetadata(Codename)); + } + } public abstract ReportResults GetResults(); From a52587410196fe6c3b5e2d7737131d7501328657 Mon Sep 17 00:00:00 2001 From: Yuriy Sountsov Date: Wed, 7 Aug 2019 11:32:02 -0400 Subject: [PATCH 2/5] Add support for recursively merging current and default culture yaml --- .../Interfaces/IReportMetadataService.cs | 4 + .../Services/ReportMetadataService.cs | 95 +++++++++++++++++-- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs b/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs index 7b82039c..50d7a4b7 100644 --- a/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs +++ b/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs @@ -4,8 +4,12 @@ namespace KenticoInspector.Core.Services.Interfaces { public interface IReportMetadataService : IService { + string DefaultCultureName { get; } + string CurrentCultureName { get; } ReportMetadata GetReportMetadata(string reportCodename) where T : new(); + + T DeserializeYaml(string path, bool ignoreUnmatchedProperties = false); } } \ No newline at end of file diff --git a/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs b/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs index 97b47aae..802e468a 100644 --- a/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs +++ b/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs @@ -1,7 +1,10 @@ -using KenticoInspector.Core.Models; -using KenticoInspector.Core.Services.Interfaces; +using System; using System.IO; using System.Threading; + +using KenticoInspector.Core.Models; +using KenticoInspector.Core.Services.Interfaces; + using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -9,22 +12,100 @@ namespace KenticoInspector.Core.Helpers { public class ReportMetadataService : IReportMetadataService { + public string DefaultCultureName => "en-US"; + public string CurrentCultureName => Thread.CurrentThread.CurrentCulture.Name; public ReportMetadata GetReportMetadata(string reportCodename) where T : new() { - var yamlPath = $"{DirectoryHelper.GetExecutingDirectory()}\\{reportCodename}\\Metadata\\{CurrentCultureName}.yaml"; - return DeserializeYaml>(yamlPath); + var metadataDirectory = $"{DirectoryHelper.GetExecutingDirectory()}\\{reportCodename}\\Metadata\\"; + + var defaultCultureMetadataPath = $"{metadataDirectory}{DefaultCultureName}.yaml"; + + var currentCultureMetadataPath = $"{metadataDirectory}{CurrentCultureName}.yaml"; + + var currentCultureIsDefaultCulture = DefaultCultureName == CurrentCultureName; + + var currentCultureMetadataPathExists = File.Exists(currentCultureMetadataPath); + + if (!currentCultureIsDefaultCulture && currentCultureMetadataPathExists) + { + var defaultCultureMetadata = DeserializeYaml>(defaultCultureMetadataPath); + + var currentCultureMetadata = DeserializeYaml>(currentCultureMetadataPath, true); + + return GetMergedMetadata(defaultCultureMetadata, currentCultureMetadata); + } + + return DeserializeYaml>(defaultCultureMetadataPath); } - public T DeserializeYaml(string path) + public T DeserializeYaml(string path, bool ignoreUnmatchedProperties = false) { - var deserializer = new DeserializerBuilder() - .WithNamingConvention(new CamelCaseNamingConvention()) + var deserializerBuilder = new DeserializerBuilder() + .WithNamingConvention(new CamelCaseNamingConvention()); + + if (ignoreUnmatchedProperties) + { + deserializerBuilder + .IgnoreUnmatchedProperties(); + } + + var deserializer = deserializerBuilder .Build(); var yamlFile = File.ReadAllText(path); + return deserializer.Deserialize(yamlFile); } + + private ReportMetadata GetMergedMetadata(ReportMetadata defaultMetadata, ReportMetadata overrideMetadata) where T : new() + { + var mergedMetadata = new ReportMetadata + { + Details = new ReportDetails(), + Terms = new T() + }; + + mergedMetadata.Details.Name = overrideMetadata.Details?.Name ?? defaultMetadata.Details.Name; + mergedMetadata.Details.ShortDescription = overrideMetadata.Details?.ShortDescription ?? defaultMetadata.Details.ShortDescription; + mergedMetadata.Details.LongDescription = overrideMetadata.Details?.LongDescription ?? defaultMetadata.Details.LongDescription; + + RecursivelySetPropertyValues(typeof(T), defaultMetadata.Terms, overrideMetadata.Terms, mergedMetadata.Terms); + + return mergedMetadata; + } + + private static void RecursivelySetPropertyValues(Type objectType, object defaultObject, object overrideObject, object targetObject) + { + var objectTypeProperties = objectType.GetProperties(); + + foreach (var objectTypeProperty in objectTypeProperties) + { + var objectTypePropertyType = objectTypeProperty.PropertyType; + + var defaultObjectPropertyValue = objectTypeProperty.GetValue(defaultObject); + + object overrideObjectPropertyValue = null; + + if (overrideObject != null) + { + overrideObjectPropertyValue = objectTypeProperty.GetValue(overrideObject); + } + + if (objectTypePropertyType.Namespace == objectType.Namespace) + { + var targetObjectPropertyValue = Activator.CreateInstance(objectTypePropertyType); + + objectTypeProperty.SetValue(targetObject, targetObjectPropertyValue); + + RecursivelySetPropertyValues(objectTypePropertyType, defaultObjectPropertyValue, overrideObjectPropertyValue, targetObjectPropertyValue); + } + else + { + objectTypeProperty.SetValue(targetObject, overrideObjectPropertyValue ?? defaultObjectPropertyValue); + } + } + } } } \ No newline at end of file From 88c1c249f48eef9838919fedd09162a9fc6cd7eb Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Wed, 7 Aug 2019 12:07:39 -0400 Subject: [PATCH 3/5] Remove internal method from interface --- .../Services/Interfaces/IReportMetadataService.cs | 2 -- .../Services/ReportMetadataService.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs b/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs index 50d7a4b7..92787b18 100644 --- a/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs +++ b/KenticoInspector.Core/Services/Interfaces/IReportMetadataService.cs @@ -9,7 +9,5 @@ public interface IReportMetadataService : IService string CurrentCultureName { get; } ReportMetadata GetReportMetadata(string reportCodename) where T : new(); - - T DeserializeYaml(string path, bool ignoreUnmatchedProperties = false); } } \ No newline at end of file diff --git a/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs b/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs index 802e468a..15628ab5 100644 --- a/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs +++ b/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs @@ -40,7 +40,7 @@ public class ReportMetadataService : IReportMetadataService return DeserializeYaml>(defaultCultureMetadataPath); } - public T DeserializeYaml(string path, bool ignoreUnmatchedProperties = false) + private T DeserializeYaml(string path, bool ignoreUnmatchedProperties = false) { var deserializerBuilder = new DeserializerBuilder() .WithNamingConvention(new CamelCaseNamingConvention()); From eab6d9e818433c49b99defe22d0864a1bc850e4c Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Wed, 7 Aug 2019 12:10:11 -0400 Subject: [PATCH 4/5] Minor formatting cleanup --- KenticoInspector.Core/AbstractReport.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/KenticoInspector.Core/AbstractReport.cs b/KenticoInspector.Core/AbstractReport.cs index 56c5cc68..03d91a14 100644 --- a/KenticoInspector.Core/AbstractReport.cs +++ b/KenticoInspector.Core/AbstractReport.cs @@ -7,9 +7,10 @@ namespace KenticoInspector.Core { public abstract class AbstractReport : IReport, IWithMetadata where T : new() { - private ReportMetadata metadata; protected readonly IReportMetadataService reportMetadataService; + private ReportMetadata metadata; + public AbstractReport(IReportMetadataService reportMetadataService) { this.reportMetadataService = reportMetadataService; @@ -17,17 +18,10 @@ public AbstractReport(IReportMetadataService reportMetadataService) public string Codename => GetCodename(this.GetType()); - public static string GetCodename(Type reportType) - { - return GetDirectParentNamespace(reportType); - } - public abstract IList CompatibleVersions { get; } public virtual IList IncompatibleVersions => new List(); - public abstract IList Tags { get; } - public ReportMetadata Metadata { get @@ -36,6 +30,13 @@ public ReportMetadata Metadata } } + public abstract IList Tags { get; } + + public static string GetCodename(Type reportType) + { + return GetDirectParentNamespace(reportType); + } + public abstract ReportResults GetResults(); private static string GetDirectParentNamespace(Type reportType) From fd1bd2218d4ca867cabe86bbcf7b31f07c079ee8 Mon Sep 17 00:00:00 2001 From: Christopher Jennings <1398135+ChristopherJennings@users.noreply.github.com> Date: Wed, 7 Aug 2019 12:39:38 -0400 Subject: [PATCH 5/5] Refactor --- .../Services/ReportMetadataService.cs | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs b/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs index 15628ab5..cc9b76d0 100644 --- a/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs +++ b/KenticoInspector.Infrastructure/Services/ReportMetadataService.cs @@ -19,37 +19,34 @@ public class ReportMetadataService : IReportMetadataService public ReportMetadata GetReportMetadata(string reportCodename) where T : new() { var metadataDirectory = $"{DirectoryHelper.GetExecutingDirectory()}\\{reportCodename}\\Metadata\\"; + + var reportMetadata = GetReportMetadataInternal(metadataDirectory, CurrentCultureName); - var defaultCultureMetadataPath = $"{metadataDirectory}{DefaultCultureName}.yaml"; - - var currentCultureMetadataPath = $"{metadataDirectory}{CurrentCultureName}.yaml"; - - var currentCultureIsDefaultCulture = DefaultCultureName == CurrentCultureName; - - var currentCultureMetadataPathExists = File.Exists(currentCultureMetadataPath); - - if (!currentCultureIsDefaultCulture && currentCultureMetadataPathExists) + var isCurrentCultureDefaultCulture = CurrentCultureName == DefaultCultureName; + if (!isCurrentCultureDefaultCulture) { - var defaultCultureMetadata = DeserializeYaml>(defaultCultureMetadataPath); + var defaultReportMetadata = GetReportMetadataInternal(metadataDirectory, DefaultCultureName); + reportMetadata = GetMergedMetadata(defaultReportMetadata, reportMetadata); + } - var currentCultureMetadata = DeserializeYaml>(currentCultureMetadataPath, true); + return reportMetadata; + } - return GetMergedMetadata(defaultCultureMetadata, currentCultureMetadata); - } - return DeserializeYaml>(defaultCultureMetadataPath); + private ReportMetadata GetReportMetadataInternal(string metadataDirectory, string cultureName) where T : new() + { + var reportMetadataPath = $"{metadataDirectory}{cultureName}.yaml"; + var reportMetadataPathExists = File.Exists(reportMetadataPath); + return reportMetadataPathExists + ? DeserializeYaml>(reportMetadataPath) + : new ReportMetadata(); } - private T DeserializeYaml(string path, bool ignoreUnmatchedProperties = false) + private T DeserializeYaml(string path) { var deserializerBuilder = new DeserializerBuilder() - .WithNamingConvention(new CamelCaseNamingConvention()); - - if (ignoreUnmatchedProperties) - { - deserializerBuilder - .IgnoreUnmatchedProperties(); - } + .WithNamingConvention(new CamelCaseNamingConvention()) + .IgnoreUnmatchedProperties(); var deserializer = deserializerBuilder .Build();