diff --git a/LCG-UDG/Constants/FileHeaderConstants.cs b/LCG-UDG/Constants/FileHeaderConstants.cs new file mode 100644 index 0000000..acf6b92 --- /dev/null +++ b/LCG-UDG/Constants/FileHeaderConstants.cs @@ -0,0 +1,78 @@ +namespace Rappen.XTB.LCG +{ + /// + /// Constants used for file header generation and template placeholders + /// + public static class FileHeaderConstants + { + /// + /// Template placeholder for tool name + /// + public const string ToolName = "{toolname}"; + + /// + /// Template placeholder for version + /// + public const string Version = "{version}"; + + /// + /// Template placeholder for organization URL + /// + public const string Organization = "{organization}"; + + /// + /// Template placeholder for filename + /// + public const string Filename = "{filename}"; + + /// + /// Template placeholder for creation date + /// + public const string CreateDate = "{createdate}"; + + /// + /// Template placeholder for legend + /// + public const string Legend = "{legend}"; + + /// + /// Template placeholder for namespace + /// + public const string Namespace = "{namespace}"; + + /// + /// Template placeholder for theme + /// + public const string Theme = "{theme}"; + + /// + /// Template placeholder for padding size + /// + public const string PaddingSize = "{paddingsize}"; + + /// + /// Template placeholder for data content + /// + public const string Data = "{data}"; + + /// + /// Label used for the "Created" line in file headers + /// + public const string CreatedLabel = "Created"; + + /// + /// Label used for the "Filename" line in file headers + /// + public const string FilenameLabel = "Filename"; + + /// + /// Double line ending used in file headers + /// + public const string LineEnding = "\r\n\r\n"; + + /// + /// Single line ending used in file headers + /// + public const string SingleLineEnding = "\r\n"; + } +} diff --git a/LCG-UDG/Generation/Extensions.cs b/LCG-UDG/Generation/Extensions.cs index 7ab3a47..a8fb5e0 100644 --- a/LCG-UDG/Generation/Extensions.cs +++ b/LCG-UDG/Generation/Extensions.cs @@ -1,17 +1,21 @@ -using System; +using McTools.Xrm.Connection; +using Microsoft.Xrm.Sdk.Metadata; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Windows.Forms; -using McTools.Xrm.Connection; -using Microsoft.Xrm.Sdk.Metadata; namespace Rappen.XTB.LCG { public static class Extensions { + private const string DatePattern = @"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}"; + private const string DateFormat = "yyyy-MM-dd HH:mm:ss"; + public static AttributeMetadata GetAttribute(this Dictionary entities, string entity, string attribute) { if (entities == null @@ -31,21 +35,70 @@ public static bool WriteFile(this string data, string filename, string orgurl, S var content = GetDataContent(data, settings, version); content = header + "\r\n\r\n" + content; content = content.BeautifyContent(settings.TemplateSettings.Template.IndentStr); + if (settings.SaveConfigurationInCommonFile) { string selection = GetInlineConfiguration(settings); content += "\r\n\r\n" + selection; } - try + + if (File.Exists(filename)) { - File.WriteAllText(filename, content); - return true; + var existingContent = File.ReadAllText(filename); + var contentWithoutDate = RemoveDateFromContent(content); + var existingContentWithoutDate = RemoveDateFromContent(existingContent); + + if (contentWithoutDate.Trim().Equals(existingContentWithoutDate.Trim(), StringComparison.Ordinal)) + { + return false; + } } - catch (Exception e) + + File.WriteAllText(filename, content); + return true; + } + + private static string RemoveDateFromContent(string content) + { + var lines = content.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); + var result = new List(); + var skipConfiguration = false; + + foreach (var line in lines) { - MessageBox.Show(e.Message, "Generate", MessageBoxButtons.OK, MessageBoxIcon.Error); - return false; + if (line.Contains(FileHeaderConstants.CreatedLabel) && line.Contains(":") && + Regex.IsMatch(line, DatePattern)) + { + continue; + } + + if (line.Contains(FileHeaderConstants.FilenameLabel) && line.Contains(":")) + { + continue; + } + + // Skip configuration section + if (line.Contains("LCG-configuration-BEGIN")) + { + skipConfiguration = true; + continue; + } + + if (line.Contains("LCG-configuration-END")) + { + skipConfiguration = false; + continue; + } + + if (skipConfiguration) + { + continue; + } + + result.Add(line); } + + return string.Join("\r\n", result); } private static string GetInlineConfiguration(Settings settings) @@ -63,27 +116,27 @@ private static string GetInlineConfiguration(Settings settings) private static string GetFileHeader(string filename, string orgurl, Settings settings, string version) { var header = settings.TemplateSettings.Template.FileHeader - .Replace("{toolname}", settings.TemplateSettings.ToolName) - .Replace("{version}", version) - .Replace("{organization}", orgurl) - .Replace("{filename}", filename) - .Replace("{createdate}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) - .Replace("{legend}", settings.Legend ? settings.TemplateSettings.Template.Legend : string.Empty) - .Replace("{namespace}", settings.NameSpace) - .Replace("\r\n\r\n", "\r\n"); + .Replace(FileHeaderConstants.ToolName, settings.TemplateSettings.ToolName) + .Replace(FileHeaderConstants.Version, version) + .Replace(FileHeaderConstants.Organization, orgurl) + .Replace(FileHeaderConstants.Filename, filename) + .Replace(FileHeaderConstants.CreateDate, DateTime.Now.ToString(DateFormat)) + .Replace(FileHeaderConstants.Legend, settings.Legend ? settings.TemplateSettings.Template.Legend : string.Empty) + .Replace(FileHeaderConstants.Namespace, settings.NameSpace) + .Replace(FileHeaderConstants.LineEnding, FileHeaderConstants.SingleLineEnding); return header; } private static string GetDataContent(string data, Settings settings, string version) { return settings.TemplateSettings.Template.DataContainer - .Replace("{toolname}", settings.TemplateSettings.ToolName) - .Replace("{version}", version) - .Replace("{namespace}", settings.NameSpace) - .Replace("{theme}", string.IsNullOrEmpty(settings.Theme) ? settings.TemplateSettings.Template.DefaultTheme : settings.GetTheme()) - .Replace("{legend}", settings.Legend ? settings.TemplateSettings.Template.Legend : string.Empty) - .Replace("{paddingsize}", settings.TableSize.ToString()) - .Replace("{data}", data); + .Replace(FileHeaderConstants.ToolName, settings.TemplateSettings.ToolName) + .Replace(FileHeaderConstants.Version, version) + .Replace(FileHeaderConstants.Namespace, settings.NameSpace) + .Replace(FileHeaderConstants.Theme, string.IsNullOrEmpty(settings.Theme) ? settings.TemplateSettings.Template.DefaultTheme : settings.GetTheme()) + .Replace(FileHeaderConstants.Legend, settings.Legend ? settings.TemplateSettings.Template.Legend : string.Empty) + .Replace(FileHeaderConstants.PaddingSize, settings.TableSize.ToString()) + .Replace(FileHeaderConstants.Data, data); } private static string BeautifyContent(this string content, string indentstr) diff --git a/LCG-UDG/Generation/GenerationUtils.cs b/LCG-UDG/Generation/GenerationUtils.cs index 5e924a3..d966dd1 100644 --- a/LCG-UDG/Generation/GenerationUtils.cs +++ b/LCG-UDG/Generation/GenerationUtils.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Windows.Forms; namespace Rappen.XTB.LCG { @@ -12,45 +11,37 @@ public class GenerationUtils { public static bool GenerateFiles(List selectedentities, Settings settings, IConstantFileWriter fileWriter) { - try + var addedrelationships = new List(); + var commonentity = GetCommonEntity(selectedentities, settings); + var suffix = Settings.FileSuffix(settings.TemplateFormat); + if (commonentity != null) { - var addedrelationships = new List(); - var commonentity = GetCommonEntity(selectedentities, settings); - var suffix = Settings.FileSuffix(settings.TemplateFormat); - if (commonentity != null) - { - var entity = GetClass(commonentity, null, settings, addedrelationships); - var fileName = commonentity.GetNameTechnical(settings.FileName, settings) + suffix; - fileWriter.WriteBlock(settings, entity, fileName); - } - if (settings.Groups?.Count > 0) - { - foreach (var group in settings.Groups.Where(g => selectedentities.Any(e => e.Group == g))) - { - var groupstr = GetGroup(group, selectedentities.Where(e => e.Group == group).ToList(), settings, addedrelationships); - fileWriter.WriteBlock(settings, groupstr, group.Name); - } - } - foreach (var entitymetadata in selectedentities.Where(e => e.Group == null)) - { - var entity = GetClass(entitymetadata, commonentity, settings, addedrelationships); - var fileName = entitymetadata.GetNameTechnical(settings.FileName, settings) + suffix; - fileWriter.WriteBlock(settings, entity, fileName); - } - if (settings.TemplateSettings.Template.AddAllRelationshipsAfterEntities) + var entity = GetClass(commonentity, null, settings, addedrelationships); + var fileName = commonentity.GetNameTechnical(settings.FileName, settings) + suffix; + fileWriter.WriteBlock(settings, entity, fileName); + } + if (settings.Groups?.Count > 0) + { + foreach (var group in settings.Groups.Where(g => selectedentities.Any(e => e.Group == g))) { - var relationships = selectedentities.SelectMany(e => e.Relationships.Where(r => r.IsSelected)); - relationships = relationships.GroupBy(r => r.LogicalName).Select(r => r.FirstOrDefault()); // This will make it distinct by LogicalName - var allrelationshipsstring = GetRelationships(relationships, settings, addedrelationships); - fileWriter.WriteBlock(settings, allrelationshipsstring, "Relationships" + suffix); + var groupstr = GetGroup(group, selectedentities.Where(e => e.Group == group).ToList(), settings, addedrelationships); + fileWriter.WriteBlock(settings, groupstr, group.Name); } - return fileWriter.Finalize(settings); } - catch (Exception ex) + foreach (var entitymetadata in selectedentities.Where(e => e.Group == null)) + { + var entity = GetClass(entitymetadata, commonentity, settings, addedrelationships); + var fileName = entitymetadata.GetNameTechnical(settings.FileName, settings) + suffix; + fileWriter.WriteBlock(settings, entity, fileName); + } + if (settings.TemplateSettings.Template.AddAllRelationshipsAfterEntities) { - MessageBox.Show($"Error:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - return false; + var relationships = selectedentities.SelectMany(e => e.Relationships.Where(r => r.IsSelected)); + relationships = relationships.GroupBy(r => r.LogicalName).Select(r => r.FirstOrDefault()); // This will make it distinct by LogicalName + var allrelationshipsstring = GetRelationships(relationships, settings, addedrelationships); + fileWriter.WriteBlock(settings, allrelationshipsstring, "Relationships" + suffix); } + return fileWriter.Finalize(settings); } private static EntityMetadataProxy GetCommonEntity(List selectedentities, Settings settings) diff --git a/LCG-UDG/LCG-UDG-Common.csproj b/LCG-UDG/LCG-UDG-Common.csproj index 7834315..095d1db 100644 --- a/LCG-UDG/LCG-UDG-Common.csproj +++ b/LCG-UDG/LCG-UDG-Common.csproj @@ -92,6 +92,7 @@ About.cs + diff --git a/LCG-UDG/LCG.cs b/LCG-UDG/LCG.cs index 81a328a..c93039f 100644 --- a/LCG-UDG/LCG.cs +++ b/LCG-UDG/LCG.cs @@ -127,20 +127,31 @@ private void btnGenerate_Click(object sender, EventArgs e) GetSettingsFromUI(); GetSelectionFromUI(); var filewriter = settings.GetWriter(ConnectionDetail.WebApplicationUrl); - if (GenerationUtils.GenerateFiles(entities.Where(ent => ent.Selected).ToList(), settings, filewriter)) + try { - var message = filewriter.GetResult(settings); - if (isUML) + if (GenerationUtils.GenerateFiles(entities.Where(ent => ent.Selected).ToList(), settings, filewriter)) { - this.ShowFileLinkMessage( - message, - settings.CommonFilePath, 10000); + var message = filewriter.GetResult(settings); + if (isUML) + { + this.ShowFileLinkMessage( + message, + settings.CommonFilePath, 10000); + } + else + { + this.ShowSuccessMessage(message, 10000); + } } else { - this.ShowSuccessMessage(message, 10000); + this.ShowInfoMessage("No file needed, probably because no changes.", 5000); } } + catch (Exception ex) + { + ShowErrorDialog(ex, $"Generate {settings.TemplateFormat}"); + } } private void btnLoadEntities_Click(object sender, EventArgs e) diff --git a/LCG-UDG/Settings/Settings.cs b/LCG-UDG/Settings/Settings.cs index 259a40e..5e0d664 100644 --- a/LCG-UDG/Settings/Settings.cs +++ b/LCG-UDG/Settings/Settings.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using Rappen.XRM.Helpers.Extensions; +using System.Collections.Generic; using System.IO; using System.Windows.Forms; +using System.Xml.Serialization; using XrmToolBox.Extensibility; namespace Rappen.XTB.LCG @@ -176,11 +178,41 @@ internal string GetTheme() public class EntityFilter { public bool Expanded { get; set; } = false; + + [XmlIgnore] public CheckState Custom { get; set; } = CheckState.Checked; + + [XmlElement("Custom")] + public string CustomSerialized + { + get => Custom.ToString(); + set => Custom = value.ToCheckState(CheckState.Checked); + } + + [XmlIgnore] public CheckState Managed { get; set; } = CheckState.Checked; + + [XmlElement("Managed")] + public string ManagedSerialized + { + get => Managed.ToString(); + set => Managed = value.ToCheckState(CheckState.Checked); + } + public bool ExcludeIntersect { get; set; } + internal CheckState Selected { get; set; } + + [XmlIgnore] public CheckState Records { get; set; } = CheckState.Checked; + + [XmlElement("Records")] + public string RecordsSerialized + { + get => Records.ToString(); + set => Records = value.ToCheckState(CheckState.Checked); + } + public bool Uncountable { get; set; } } @@ -188,12 +220,67 @@ public class AttributeFilter { public bool Expanded { get; set; } = false; public bool CheckAll { get; set; } + + [XmlIgnore] public CheckState Custom { get; set; } = CheckState.Checked; + + [XmlElement("Custom")] + public string CustomSerialized + { + get => Custom.ToString(); + set => Custom = value.ToCheckState(CheckState.Checked); + } + + [XmlIgnore] public CheckState Managed { get; set; } = CheckState.Checked; + + [XmlElement("Managed")] + public string ManagedSerialized + { + get => Managed.ToString(); + set => Managed = value.ToCheckState(CheckState.Checked); + } + + [XmlIgnore] public CheckState PrimaryKeyName { get; set; } = CheckState.Checked; + + [XmlElement("PrimaryKeyName")] + public string PrimaryKeyNameSerialized + { + get => PrimaryKeyName.ToString(); + set => PrimaryKeyName = value.ToCheckState(CheckState.Checked); + } + + [XmlIgnore] public CheckState Required { get; set; } = CheckState.Checked; + + [XmlElement("Required")] + public string RequiredSerialized + { + get => Required.ToString(); + set => Required = value.ToCheckState(CheckState.Checked); + } + + [XmlIgnore] public CheckState Logical { get; set; } = CheckState.Unchecked; + + [XmlElement("Logical")] + public string LogicalSerialized + { + get => Logical.ToString(); + set => Logical = value.ToCheckState(CheckState.Unchecked); + } + + [XmlIgnore] public CheckState Internal { get; set; } = CheckState.Unchecked; + + [XmlElement("Internal")] + public string InternalSerialized + { + get => Internal.ToString(); + set => Internal = value.ToCheckState(CheckState.Unchecked); + } + public bool ExcludeCreMod { get; set; } = true; public bool ExcludeOwner { get; set; } = true; internal bool AreUsed { get; set; } = false; @@ -204,8 +291,27 @@ public class RelationshipFilter { public bool Expanded { get; set; } = false; public bool CheckAll { get; set; } + + [XmlIgnore] public CheckState Custom { get; set; } = CheckState.Checked; + + [XmlElement("Custom")] + public string CustomSerialized + { + get => Custom.ToString(); + set => Custom = value.ToCheckState(CheckState.Checked); + } + + [XmlIgnore] public CheckState Managed { get; set; } = CheckState.Checked; + + [XmlElement("Managed")] + public string ManagedSerialized + { + get => Managed.ToString(); + set => Managed = value.ToCheckState(CheckState.Checked); + } + public bool Type1N { get; set; } = false; public bool TypeN1 { get; set; } = true; public bool TypeNN { get; set; } = true; diff --git a/LCGCmd/LCGHelper.cs b/LCGCmd/LCGHelper.cs index 58efecc..7deac94 100644 --- a/LCGCmd/LCGHelper.cs +++ b/LCGCmd/LCGHelper.cs @@ -47,13 +47,20 @@ public void GenerateConstants() LoadEntities(); RestoreSelectedEntities(); var writer = Settings.GetWriter(this.crmConnection.WebApplicationUrl); - if (GenerationUtils.GenerateFiles(entities, Settings, writer)) + try { - Console.Write(writer.GetResult(Settings)); + if (GenerationUtils.GenerateFiles(entities, Settings, writer)) + { + Console.Write(writer.GetResult(Settings)); + } + else + { + Console.WriteLine("Not saved, probably because no changes."); + } } - else + catch (Exception ex) { - Console.WriteLine("Error"); + MessageBox.Show($"Error:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } diff --git a/LCGTests/DatePreservationTests.cs b/LCGTests/DatePreservationTests.cs new file mode 100644 index 0000000..3a8dfc0 --- /dev/null +++ b/LCGTests/DatePreservationTests.cs @@ -0,0 +1,118 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rappen.XTB.LCG; + +namespace LateboundConstantGeneratorTests +{ + [TestClass] + public class DatePreservationTests + { + private string _testFilePath; + + [TestInitialize] + public void Setup() + { + _testFilePath = Path.GetTempFileName(); + } + + [TestCleanup] + public void Cleanup() + { + if (File.Exists(_testFilePath)) + { + File.Delete(_testFilePath); + } + } + + [TestMethod] + public void WriteFile_WhenContentUnchanged_ShouldPreserveOriginalDate() + { + // Arrange + var settings = new Settings(); + settings.NameSpace = "TestNamespace"; + var originalDate = "2023-01-15 10:30:45"; + var originalContent = TestContentHelper.GetExpectedTestContent(Path.GetFileName(_testFilePath), originalDate); + + // Write original file + File.WriteAllText(_testFilePath, originalContent); + + // Act - Write the same content again + var dataContent = TestContentHelper.GetTestDataContent(); + var result = dataContent.WriteFile(_testFilePath, "https://test.crm.dynamics.com", settings); + + // Assert + Assert.IsTrue(result); + var finalContent = File.ReadAllText(_testFilePath); + Assert.IsTrue(finalContent.Contains(originalDate), "Original date should be preserved"); + Assert.IsFalse(finalContent.Contains(DateTime.Now.ToString("yyyy-MM-dd")), "Current date should not be used"); + } + + [TestMethod] + public void WriteFile_WhenContentChanged_ShouldUpdateDate() + { + // Arrange + var settings = new Settings(); + settings.NameSpace = "TestNamespace"; + var originalDate = "2023-01-15 10:30:45"; + var originalContent = TestContentHelper.GetExpectedTestContent(Path.GetFileName(_testFilePath), originalDate); + + // Write original file + File.WriteAllText(_testFilePath, originalContent); + + // Act - Write different content + var dataContent = TestContentHelper.GetModifiedTestDataContent(); + var result = dataContent.WriteFile(_testFilePath, "https://test.crm.dynamics.com", settings); + + // Assert + Assert.IsTrue(result); + var finalContent = File.ReadAllText(_testFilePath); + Assert.IsFalse(finalContent.Contains(originalDate), "Original date should not be preserved when content changes"); + Assert.IsTrue(finalContent.Contains(DateTime.Now.ToString("yyyy-MM-dd")), "Current date should be used when content changes"); + Assert.IsTrue(finalContent.Contains("modified_entity"), "Modified content should be present"); + } + + [TestMethod] + public void WriteFile_WhenOnlyFilenameChanged_ShouldPreserveOriginalFilename() + { + // Arrange + var settings = new Settings(); + settings.NameSpace = "TestNamespace"; + var originalDate = "2023-01-15 10:30:45"; + var originalFilename = "OriginalFile.cs"; + var originalContent = TestContentHelper.GetExpectedTestContent(originalFilename, originalDate); + + // Write original file + File.WriteAllText(_testFilePath, originalContent); + + // Act - Write content with different filename + var dataContent = TestContentHelper.GetTestDataContent(); + var result = dataContent.WriteFile(_testFilePath, "https://test.crm.dynamics.com", settings); + + // Assert + Assert.IsTrue(result); + var finalContent = File.ReadAllText(_testFilePath); + Assert.IsTrue(finalContent.Contains(originalFilename), "Original filename should be preserved"); + Assert.IsFalse(finalContent.Contains("ModifiedFile.cs"), "Modified filename should not be used"); + Assert.IsTrue(finalContent.Contains(originalDate), "Original date should also be preserved"); + } + + [TestMethod] + public void WriteFile_WhenFileDoesNotExist_ShouldUseCurrentDate() + { + // Arrange + var settings = new Settings(); + settings.NameSpace = "TestNamespace"; + var dataContent = TestContentHelper.GetNewTestDataContent(); + + // Act - Write to new file + var result = dataContent.WriteFile(_testFilePath, "https://test.crm.dynamics.com", settings); + + // Assert + Assert.IsTrue(result); + var finalContent = File.ReadAllText(_testFilePath); + Assert.IsTrue(finalContent.Contains(DateTime.Now.ToString("yyyy-MM-dd")), "Current date should be used for new files"); + Assert.IsTrue(finalContent.Contains("new_entity"), "New content should be present"); + } + } +} diff --git a/LCGTests/LCGTests.csproj b/LCGTests/LCGTests.csproj index ed52ed1..cb9f110 100644 --- a/LCGTests/LCGTests.csproj +++ b/LCGTests/LCGTests.csproj @@ -228,8 +228,10 @@ - + + + @@ -239,6 +241,9 @@ Always + + Always + diff --git a/LCGTests/TestContentHelper.cs b/LCGTests/TestContentHelper.cs new file mode 100644 index 0000000..950a932 --- /dev/null +++ b/LCGTests/TestContentHelper.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; + +namespace LateboundConstantGeneratorTests +{ + public static class TestContentHelper + { + private static readonly string TestDataPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "testdata"); + + public static string GetExpectedTestContent(string filename, string date) + { + var templatePath = Path.Combine(TestDataPath, "ExpectedTestContent.cs"); + var template = File.ReadAllText(templatePath); + return string.Format(template, filename, date); + } + + public static string GetTestDataContent() + { + return @"public class TestClass +{ + public const string EntityName = ""test_entity""; +}"; + } + + public static string GetModifiedTestDataContent() + { + return @"public class TestClass +{ + public const string EntityName = ""modified_entity""; + public const string NewProperty = ""new_value""; +}"; + } + + public static string GetNewTestDataContent() + { + return @"public class TestClass +{ + public const string EntityName = ""new_entity""; +}"; + } + } +} diff --git a/LCGTests/testdata/ExpectedTestContent.cs b/LCGTests/testdata/ExpectedTestContent.cs new file mode 100644 index 0000000..55184fe --- /dev/null +++ b/LCGTests/testdata/ExpectedTestContent.cs @@ -0,0 +1,16 @@ +// ********************************************************************* +// Created by : Latebound Constants Generator 1.2023.5.901 for XrmToolBox +// Tool Author: Jonas Rapp https://jonasr.app/ +// GitHub : https://github.com/rappen/LCG-UDG/ +// Source Org : https://test.crm.dynamics.com +// Filename : {0} +// Created : {1} +// ********************************************************************* + +namespace TestNamespace +{{ + public class TestClass + {{ + public const string EntityName = ""test_entity""; + }} +}} diff --git a/Rappen.XTB.Helper b/Rappen.XTB.Helper index 7b60f84..7905fb3 160000 --- a/Rappen.XTB.Helper +++ b/Rappen.XTB.Helper @@ -1 +1 @@ -Subproject commit 7b60f8449564d3080ae165292eb53d951e8bc348 +Subproject commit 7905fb35e1e96387da39baff67414cf861d68ae8