diff --git a/src/AncillaryInfrastructure.IntegrationTests/ProvisioningsApiSpec.cs b/src/AncillaryInfrastructure.IntegrationTests/ProvisioningsApiSpec.cs index 4a415f9a..ecbf9b7b 100644 --- a/src/AncillaryInfrastructure.IntegrationTests/ProvisioningsApiSpec.cs +++ b/src/AncillaryInfrastructure.IntegrationTests/ProvisioningsApiSpec.cs @@ -1,4 +1,3 @@ -using System.Text.Json; using ApiHost1; using Application.Interfaces.Services; using Application.Persistence.Shared; @@ -62,9 +61,9 @@ public async Task WhenDeliverProvisioning_ThenDelivers() }, req => req.SetJWTBearerToken(login.AccessToken)); organization.Content.Value.Settings!.Count.Should().Be(3); - organization.Content.Value.Settings["aname1"].As().GetString().Should().Be("avalue"); - organization.Content.Value.Settings["aname2"].As().GetString().Should().Be("99"); - organization.Content.Value.Settings["aname3"].As().GetString().Should().Be("True"); + organization.Content.Value.Settings["aname1"].Should().Be("avalue"); + organization.Content.Value.Settings["aname2"].Should().Be("99"); + organization.Content.Value.Settings["aname3"].Should().Be("True"); #endif } @@ -107,9 +106,9 @@ public async Task WhenDrainAllProvisioningsAndSomeWithUnknownTenancies_ThenDrain }, req => req.SetJWTBearerToken(login.AccessToken)); organization.Content.Value.Settings!.Count.Should().Be(3); - organization.Content.Value.Settings["aname1"].As().GetString().Should().Be("avalue1"); - organization.Content.Value.Settings["aname2"].As().GetString().Should().Be("99"); - organization.Content.Value.Settings["aname3"].As().GetString().Should().Be("True"); + organization.Content.Value.Settings["aname1"].Should().Be("avalue1"); + organization.Content.Value.Settings["aname2"].Should().Be("99"); + organization.Content.Value.Settings["aname3"].Should().Be("True"); } #endif diff --git a/src/Application.Resources.Shared/Organization.cs b/src/Application.Resources.Shared/Organization.cs index d596b443..63dde585 100644 --- a/src/Application.Resources.Shared/Organization.cs +++ b/src/Application.Resources.Shared/Organization.cs @@ -1,4 +1,5 @@ using Application.Interfaces.Resources; +using JetBrains.Annotations; namespace Application.Resources.Shared; @@ -13,9 +14,10 @@ public class Organization : IIdentifiableResource public required string Id { get; set; } } +[UsedImplicitly] public class OrganizationWithSettings : Organization { - public required Dictionary Settings { get; set; } + public required Dictionary Settings { get; set; } } public enum OrganizationOwnership diff --git a/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs b/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs index 7677d71e..53ada5f7 100644 --- a/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs +++ b/src/Common.UnitTests/Extensions/StringExtensionsSpec.cs @@ -372,7 +372,7 @@ public void WhenToIntAndNotMatches_ThenThrows() } [Fact] - public void WhenToIntAndMatchesLowercase_ThenReturnsTrue() + public void WhenToIntAndMatches_ThenReturns() { var result = "9".ToInt(); @@ -395,6 +395,37 @@ public void WhenToIntOrDefaultAndNotMatches_ThenReturnsDefault() result.Should().Be(9); } + [Fact] + public void WhenToDoubleAndEmpty_ThenReturnsMinusOne() + { + var result = "".ToDouble(); + + result.Should().Be(-1); + } + + [Fact] + public void WhenToDoubleAndNotMatches_ThenThrows() + { + "notavalue".Invoking(x => x.ToDouble()) + .Should().Throw(); + } + + [Fact] + public void WhenToDoubleAndMatchesInteger_ThenReturns() + { + var result = "9".ToDouble(); + + result.Should().Be(9D); + } + + [Fact] + public void WhenToDoubleAndMatchesFloating_ThenReturns() + { + var result = "9.009".ToDouble(); + + result.Should().Be(9.009D); + } + [Fact] public void WhenWithoutTrailingSlashWithSlash_ThenReturnsPathWithoutSlash() { diff --git a/src/Common/Extensions/StringExtensions.cs b/src/Common/Extensions/StringExtensions.cs index 8445675a..5bfe3429 100644 --- a/src/Common/Extensions/StringExtensions.cs +++ b/src/Common/Extensions/StringExtensions.cs @@ -269,6 +269,19 @@ public static string ToCamelCase(this string value) } #endif #if COMMON_PROJECT || ANALYZERS_NONPLATFORM + /// + /// Converts the to a floating value + /// + public static double ToDouble(this string? value) + { + if (value.HasNoValue()) + { + return -1; + } + + return double.Parse(value); + } + /// /// Converts the to a integer value /// diff --git a/src/Infrastructure.Web.Api.Operations.Shared/Organizations/GetOrganizationSettingsResponse.cs b/src/Infrastructure.Web.Api.Operations.Shared/Organizations/GetOrganizationSettingsResponse.cs index e180136e..1b5e944a 100644 --- a/src/Infrastructure.Web.Api.Operations.Shared/Organizations/GetOrganizationSettingsResponse.cs +++ b/src/Infrastructure.Web.Api.Operations.Shared/Organizations/GetOrganizationSettingsResponse.cs @@ -7,5 +7,5 @@ public class GetOrganizationSettingsResponse : IWebResponse { public Organization? Organization { get; set; } - public Dictionary? Settings { get; set; } + public Dictionary? Settings { get; set; } } \ No newline at end of file diff --git a/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs b/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs index 0253664a..76969144 100644 --- a/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs +++ b/src/OrganizationsApplication.UnitTests/OrganizationsApplicationSpec.cs @@ -72,7 +72,7 @@ await _application.CreateOrganizationAsync(_caller.Object, "auserid", "aname", && org.Ownership == Ownership.Personal && org.CreatedById == "auserid" && org.Settings.Properties.Count == 1 - && org.Settings.Properties["aname"].Value == "avalue" + && org.Settings.Properties["aname"].Value.As() == "avalue" && org.Settings.Properties["aname"].IsEncrypted == false ), It.IsAny())); _tenantSettingsService.Verify(tss => @@ -106,7 +106,7 @@ await _application.CreateSharedOrganizationAsync(_caller.Object, "aname", && org.Ownership == Ownership.Shared && org.CreatedById == "acallerid" && org.Settings.Properties.Count == 1 - && org.Settings.Properties["aname"].Value == "avalue" + && org.Settings.Properties["aname"].Value.As() == "avalue" && org.Settings.Properties["aname"].IsEncrypted == false ), It.IsAny())); _tenantSettingsService.Verify(tss => @@ -219,10 +219,10 @@ public async Task WhenChangeSettings_ThenReturnsSettings() && o.Ownership == Ownership.Personal && o.CreatedById == "auserid" && o.Settings.Properties.Count == 4 - && o.Settings.Properties["aname1"].Value == "anewvalue" - && o.Settings.Properties["aname2"].Value == "99" - && o.Settings.Properties["aname3"].Value == "True" - && o.Settings.Properties["aname4"].Value == "anoldvalue" + && o.Settings.Properties["aname1"].Value.As() == "anewvalue" + && o.Settings.Properties["aname2"].Value.As().Equals(99D) + && o.Settings.Properties["aname3"].Value.As() == true + && o.Settings.Properties["aname4"].Value.As() == "anoldvalue" ), It.IsAny())); _tenantSettingsService.Verify(tss => tss.CreateForTenantAsync(It.IsAny(), It.IsAny(), It.IsAny()), diff --git a/src/OrganizationsApplication/OrganizationsApplication.cs b/src/OrganizationsApplication/OrganizationsApplication.cs index 5e80dd38..ec8606da 100644 --- a/src/OrganizationsApplication/OrganizationsApplication.cs +++ b/src/OrganizationsApplication/OrganizationsApplication.cs @@ -208,7 +208,8 @@ public static OrganizationWithSettings ToOrganizationWithSettings(this Organizat { var dto = organization.ToOrganization().Convert(); dto.Settings = - organization.Settings.Properties.ToDictionary(pair => pair.Key, pair => (object?)pair.Value.Value); + organization.Settings.Properties.ToDictionary(pair => pair.Key, + pair => pair.Value.Value.ToString() ?? string.Empty); return dto; } @@ -222,7 +223,7 @@ public static Result ToSettings(this TenantSettings tenantSetti continue; } - var value = tenantSetting.Value.ToString()!; + var value = tenantSetting.Value; var setting = Setting.Create(value, tenantSetting.IsEncrypted); if (!setting.IsSuccessful) { diff --git a/src/OrganizationsDomain.UnitTests/SettingSpec.cs b/src/OrganizationsDomain.UnitTests/SettingSpec.cs index c74fe74b..9e670a96 100644 --- a/src/OrganizationsDomain.UnitTests/SettingSpec.cs +++ b/src/OrganizationsDomain.UnitTests/SettingSpec.cs @@ -1,4 +1,6 @@ +using Domain.Interfaces.Services; using FluentAssertions; +using Moq; using UnitTesting.Common; using Xunit; @@ -8,22 +10,127 @@ namespace OrganizationsDomain.UnitTests; public class SettingSpec { [Fact] - public void WhenCreateWithEmptyValue_ThenReturnsSetting() + public void WhenCreateWithEmptyStringValue_ThenReturnsSetting() { var result = Setting.Create(string.Empty, true); result.Should().BeSuccess(); - result.Value.Value.Should().BeEmpty(); + result.Value.Value.As().Should().BeEmpty(); + result.Value.ValueType.Should().Be(SettingValueType.String); result.Value.IsEncrypted.Should().BeTrue(); } [Fact] - public void WhenCreateWithNonEmptyValue_ThenReturnsSetting() + public void WhenCreateWithStringValue_ThenReturnsSetting() { var result = Setting.Create("aname", true); result.Should().BeSuccess(); result.Value.Value.Should().Be("aname"); + result.Value.ValueType.Should().Be(SettingValueType.String); result.Value.IsEncrypted.Should().BeTrue(); } + + [Fact] + public void WhenCreateWithIntegerValue_ThenReturnsSetting() + { + var result = Setting.Create(99); + + result.Should().BeSuccess(); + result.Value.Value.Should().Be(99); + result.Value.ValueType.Should().Be(SettingValueType.Number); + result.Value.IsEncrypted.Should().BeFalse(); + } + + [Fact] + public void WhenCreateWithFloatValue_ThenReturnsSetting() + { + var result = Setting.Create(99.99); + + result.Should().BeSuccess(); + result.Value.Value.Should().Be(99.99); + result.Value.ValueType.Should().Be(SettingValueType.Number); + result.Value.IsEncrypted.Should().BeFalse(); + } + + [Fact] + public void WhenCreateWithBooleanValue_ThenReturnsSetting() + { + var result = Setting.Create(true); + + result.Should().BeSuccess(); + result.Value.Value.Should().Be(true); + result.Value.ValueType.Should().Be(SettingValueType.Boolean); + result.Value.IsEncrypted.Should().BeFalse(); + } + + [Fact] + public void WhenFromAndEncryptedString_ThenReturns() + { + var tenantSettingService = new Mock(); + tenantSettingService.Setup(x => x.Decrypt(It.IsAny())).Returns("adecryptedvalue"); + + var result = Setting.From("astringvalue", SettingValueType.String, true, tenantSettingService.Object); + + result.Value.Value.Should().Be("adecryptedvalue"); + result.Value.Value.Should().BeOfType(); + result.Value.ValueType.Should().Be(SettingValueType.String); + tenantSettingService.Verify(x => x.Decrypt("astringvalue")); + } + + [Fact] + public void WhenFromAndUnencryptedString_ThenReturns() + { + var tenantSettingService = new Mock(); + tenantSettingService.Setup(x => x.Decrypt(It.IsAny())).Returns("adecryptedvalue"); + + var result = Setting.From("astringvalue", SettingValueType.String, false, tenantSettingService.Object); + + result.Value.Value.Should().Be("astringvalue"); + result.Value.Value.Should().BeOfType(); + result.Value.ValueType.Should().Be(SettingValueType.String); + tenantSettingService.Verify(x => x.Decrypt(It.IsAny()), Times.Never); + } + + [Fact] + public void WhenFromAndInteger_ThenReturns() + { + var tenantSettingService = new Mock(); + tenantSettingService.Setup(x => x.Decrypt(It.IsAny())).Returns("adecryptedvalue"); + + var result = Setting.From("99", SettingValueType.Number, false, tenantSettingService.Object); + + result.Value.Value.Should().Be(99D); + result.Value.Value.Should().BeOfType(); + result.Value.ValueType.Should().Be(SettingValueType.Number); + tenantSettingService.Verify(x => x.Decrypt(It.IsAny()), Times.Never); + } + + [Fact] + public void WhenFromAndFloat_ThenReturns() + { + var tenantSettingService = new Mock(); + tenantSettingService.Setup(x => x.Decrypt(It.IsAny())).Returns("adecryptedvalue"); + + var result = Setting.From("99.99", SettingValueType.Number, false, tenantSettingService.Object); + + result.Value.Value.Should().Be(99.99D); + result.Value.Value.Should().BeOfType(); + result.Value.ValueType.Should().Be(SettingValueType.Number); + tenantSettingService.Verify(x => x.Decrypt(It.IsAny()), Times.Never); + } + + [Fact] + public void WhenFromAndBoolean_ThenReturns() + { + var tenantSettingService = new Mock(); + tenantSettingService.Setup(x => x.Decrypt(It.IsAny())).Returns("adecryptedvalue"); + + var result = Setting.From("True", SettingValueType.Boolean, false, tenantSettingService.Object); + + result.Value.Value.Should().Be(true); + result.Value.Value.Should().BeOfType(); + result.Value.ValueType.Should().Be(SettingValueType.Boolean); + tenantSettingService.Verify(x => x.Decrypt(It.IsAny()), Times.Never); + } } \ No newline at end of file diff --git a/src/OrganizationsDomain/Events.cs b/src/OrganizationsDomain/Events.cs index 04c5e4b4..66a36c54 100644 --- a/src/OrganizationsDomain/Events.cs +++ b/src/OrganizationsDomain/Events.cs @@ -32,13 +32,15 @@ public static Created Create(Identifier id, Ownership ownership, Identifier crea public sealed class SettingCreated : IDomainEvent { - public static SettingCreated Create(Identifier id, string name, string value, bool isEncrypted) + public static SettingCreated Create(Identifier id, string name, string value, SettingValueType valueType, + bool isEncrypted) { return new SettingCreated { RootId = id, Name = name, - Value = value, + StringValue = value, + ValueType = valueType, IsEncrypted = isEncrypted, OccurredUtc = DateTime.UtcNow }; @@ -48,7 +50,9 @@ public static SettingCreated Create(Identifier id, string name, string value, bo public required string Name { get; set; } - public required string Value { get; set; } + public required string StringValue { get; set; } + + public required SettingValueType ValueType { get; set; } public required string RootId { get; set; } @@ -57,14 +61,17 @@ public static SettingCreated Create(Identifier id, string name, string value, bo public sealed class SettingUpdated : IDomainEvent { - public static SettingUpdated Create(Identifier id, string name, string from, string to, bool isEncrypted) + public static SettingUpdated Create(Identifier id, string name, string from, SettingValueType fromType, + string to, SettingValueType toType, bool isEncrypted) { return new SettingUpdated { RootId = id, Name = name, From = from, + FromType = fromType, To = to, + ToType = toType, IsEncrypted = isEncrypted, OccurredUtc = DateTime.UtcNow }; @@ -72,12 +79,16 @@ public static SettingUpdated Create(Identifier id, string name, string from, str public required string From { get; set; } + public required SettingValueType FromType { get; set; } + public required bool IsEncrypted { get; set; } public required string Name { get; set; } public required string To { get; set; } + public required SettingValueType ToType { get; set; } + public required string RootId { get; set; } public required DateTime OccurredUtc { get; set; } diff --git a/src/OrganizationsDomain/OrganizationRoot.cs b/src/OrganizationsDomain/OrganizationRoot.cs index 518e46df..fe573b0f 100644 --- a/src/OrganizationsDomain/OrganizationRoot.cs +++ b/src/OrganizationsDomain/OrganizationRoot.cs @@ -84,11 +84,14 @@ protected override Result OnStateChanged(IDomainEvent @event, bool isReco case Events.SettingCreated created: { - var value = created.IsEncrypted - ? _tenantSettingService.Decrypt(created.Value) - : created.Value; + var value = Setting.From(created.StringValue, created.ValueType, created.IsEncrypted, + _tenantSettingService); + if (!value.IsSuccessful) + { + return value.Error; + } - var settings = Settings.AddOrUpdate(created.Name, value, created.IsEncrypted); + var settings = Settings.AddOrUpdate(created.Name, value.Value); if (!settings.IsSuccessful) { return settings.Error; @@ -101,11 +104,13 @@ protected override Result OnStateChanged(IDomainEvent @event, bool isReco case Events.SettingUpdated updated: { - var to = updated.IsEncrypted - ? _tenantSettingService.Decrypt(updated.To) - : updated.To; + var to = Setting.From(updated.To, updated.ToType, updated.IsEncrypted, _tenantSettingService); + if (!to.IsSuccessful) + { + return to.Error; + } - var settings = Settings.AddOrUpdate(updated.Name, to, updated.IsEncrypted); + var settings = Settings.AddOrUpdate(updated.Name, to.Value); if (!settings.IsSuccessful) { return settings.Error; @@ -126,9 +131,10 @@ public Result CreateSettings(Settings settings) foreach (var (key, value) in settings.Properties) { var valueValue = value.IsEncrypted - ? _tenantSettingService.Encrypt(value.Value) - : value.Value; - RaiseChangeEvent(OrganizationsDomain.Events.SettingCreated.Create(Id, key, valueValue, value.IsEncrypted)); + ? _tenantSettingService.Encrypt(value.Value.ToString() ?? string.Empty) + : value.Value.ToString() ?? string.Empty; + RaiseChangeEvent(OrganizationsDomain.Events.SettingCreated.Create(Id, key, valueValue, value.ValueType, + value.IsEncrypted)); } return Result.Ok; @@ -141,18 +147,21 @@ public Result UpdateSettings(Settings settings) if (Settings.TryGet(key, out var oldSetting)) { var valueValue = value.IsEncrypted - ? _tenantSettingService.Encrypt(value.Value) - : value.Value; - RaiseChangeEvent(OrganizationsDomain.Events.SettingUpdated.Create(Id, key, oldSetting!.Value, - valueValue, value.IsEncrypted)); + ? _tenantSettingService.Encrypt(value.Value.ToString() ?? string.Empty) + : value.Value.ToString() ?? string.Empty; + var oldValue = oldSetting!.Value.ToString() ?? string.Empty; + RaiseChangeEvent(OrganizationsDomain.Events.SettingUpdated.Create(Id, key, oldValue, + oldSetting.ValueType, + valueValue, value.ValueType, value.IsEncrypted)); } else { var valueValue = value.IsEncrypted - ? _tenantSettingService.Encrypt(value.Value) - : value.Value; + ? _tenantSettingService.Encrypt(value.Value.ToString() ?? string.Empty) + : value.Value.ToString() ?? string.Empty; RaiseChangeEvent( - OrganizationsDomain.Events.SettingCreated.Create(Id, key, valueValue, value.IsEncrypted)); + OrganizationsDomain.Events.SettingCreated.Create(Id, key, valueValue, value.ValueType, + value.IsEncrypted)); } } diff --git a/src/OrganizationsDomain/Resources.Designer.cs b/src/OrganizationsDomain/Resources.Designer.cs index 170e7ea1..7fe9ae50 100644 --- a/src/OrganizationsDomain/Resources.Designer.cs +++ b/src/OrganizationsDomain/Resources.Designer.cs @@ -67,5 +67,23 @@ internal static string OrganizationDisplayName_InvalidName { return ResourceManager.GetString("OrganizationDisplayName_InvalidName", resourceCulture); } } + + /// + /// Looks up a localized string similar to The data type of the value: '{0}' is unsupported. + /// + internal static string Setting_InvalidDataType { + get { + return ResourceManager.GetString("Setting_InvalidDataType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The value type of the value: '{0}' is unsupported. + /// + internal static string Setting_InvalidValueType { + get { + return ResourceManager.GetString("Setting_InvalidValueType", resourceCulture); + } + } } } diff --git a/src/OrganizationsDomain/Resources.resx b/src/OrganizationsDomain/Resources.resx index ac0fb4d4..7ad336d3 100644 --- a/src/OrganizationsDomain/Resources.resx +++ b/src/OrganizationsDomain/Resources.resx @@ -27,4 +27,10 @@ The name is invalid + + The data type of the value: '{0}' is unsupported + + + The value type of the value: '{0}' is unsupported + \ No newline at end of file diff --git a/src/OrganizationsDomain/Setting.cs b/src/OrganizationsDomain/Setting.cs index da9d7ae3..be7e26bf 100644 --- a/src/OrganizationsDomain/Setting.cs +++ b/src/OrganizationsDomain/Setting.cs @@ -2,37 +2,102 @@ using Common.Extensions; using Domain.Common.ValueObjects; using Domain.Interfaces; +using Domain.Interfaces.Services; namespace OrganizationsDomain; public sealed class Setting : ValueObjectBase { + public static Result Create(object value, bool isEncrypted) + { + var valueType = value switch + { + string _ => SettingValueType.String, + int _ => SettingValueType.Number, + long _ => SettingValueType.Number, + double _ => SettingValueType.Number, + decimal _ => SettingValueType.Number, + bool _ => SettingValueType.Boolean, + _ => throw new InvalidOperationException(Resources.Setting_InvalidDataType.Format(value.GetType().Name)) + }; + + var canBeEncrypted = valueType == SettingValueType.String && isEncrypted; + return new Setting(value, valueType, canBeEncrypted); + } + public static Result Create(string value, bool isEncrypted) { - return new Setting(value, isEncrypted); + return new Setting(value, SettingValueType.String, isEncrypted); + } + + public static Result Create(int value) + { + return new Setting(value, SettingValueType.Number, false); + } + + public static Result Create(double value) + { + return new Setting(value, SettingValueType.Number, false); } - private Setting(string value, bool isEncrypted) + public static Result Create(bool value) + { + return new Setting(value, SettingValueType.Boolean, false); + } + + private Setting(object value, SettingValueType valueType, bool isEncrypted) { Value = value; + ValueType = valueType; IsEncrypted = isEncrypted; } public bool IsEncrypted { get; } - public string Value { get; } + public object Value { get; } + + public SettingValueType ValueType { get; } public static ValueObjectFactory Rehydrate() { return (property, _) => { var parts = RehydrateToList(property, false); - return new Setting(parts[0]!, parts[1].ToBool()); + return new Setting(parts[0]!, parts[1].ToEnumOrDefault(SettingValueType.String), parts[2].ToBool()); }; } protected override IEnumerable GetAtomicValues() { - return new object[] { Value, IsEncrypted }; + return new[] { Value, ValueType, IsEncrypted }; + } + + public static Result From(string stringValue, SettingValueType valueType, bool isEncrypted, + ITenantSettingService tenantSettingService) + { + object? value; + if (isEncrypted && valueType == SettingValueType.String) + { + value = tenantSettingService.Decrypt(stringValue); + } + else + { + value = valueType switch + { + SettingValueType.String => stringValue, + SettingValueType.Number => stringValue.ToDouble(), + SettingValueType.Boolean => stringValue.ToBool(), + _ => throw new InvalidOperationException(Resources.Setting_InvalidValueType.Format(valueType)) + }; + } + + return new Setting(value, valueType, isEncrypted); } +} + +public enum SettingValueType +{ + String = 0, + Number = 1, + Boolean = 2 } \ No newline at end of file diff --git a/src/OrganizationsDomain/Settings.cs b/src/OrganizationsDomain/Settings.cs index c15d5d56..fc17636d 100644 --- a/src/OrganizationsDomain/Settings.cs +++ b/src/OrganizationsDomain/Settings.cs @@ -41,7 +41,7 @@ protected override IEnumerable GetAtomicValues() return new[] { Properties.ToJson()! }; } - public Result AddOrUpdate(string name, string value, bool isEncrypted) + public Result AddOrUpdate(string name, object value, bool isEncrypted) { var settingValue = Setting.Create(value, isEncrypted); if (!settingValue.IsSuccessful) diff --git a/src/SaaStack.sln.DotSettings b/src/SaaStack.sln.DotSettings index be643cab..bc2606b1 100644 --- a/src/SaaStack.sln.DotSettings +++ b/src/SaaStack.sln.DotSettings @@ -830,6 +830,7 @@ public void When$condition$_Then$outcome$() True True True + True True True True