diff --git a/QueryKit.IntegrationTests/Tests/PropertyResolverTests.cs b/QueryKit.IntegrationTests/Tests/PropertyResolverTests.cs index ac5a26f..2c2112b 100644 --- a/QueryKit.IntegrationTests/Tests/PropertyResolverTests.cs +++ b/QueryKit.IntegrationTests/Tests/PropertyResolverTests.cs @@ -443,6 +443,35 @@ public async Task query_name_in_arithmetic_is_filtered() people.Should().ContainSingle(x => x.Id == fakePerson.Id); } + [Fact] + public async Task prevented_property_clause_by_its_query_name_under_or_does_not_return_every_row() + { + // Arrange + var testingServiceScope = new TestingServiceScope(); + var title = new Faker().Lorem.Sentence(); + var fakePerson = new FakeTestingPersonBuilder() + .WithTitle(title) + .WithFirstName("Paul") + .WithAge(30) + .Build(); + await testingServiceScope.InsertAsync(fakePerson); + + var input = $"""Title == "{title}" && (first == "Paul" || Age > 100)"""; + var config = new QueryKitConfiguration(config => + { + config.IgnoredClauseBehavior = IgnoredClauseBehavior.Remove; + config.Property(x => x.FirstName!).HasQueryName("first").PreventFilter().PreventSort(); + }); + + // Act + var people = await testingServiceScope.DbContext().People + .ApplyQueryKitFilter(input, config) + .ToListAsync(); + + // Assert + people.Should().BeEmpty(); + } + [Theory] [InlineData("first-name")] [InlineData("_first")] diff --git a/QueryKit.UnitTests/PropertyResolverTests.cs b/QueryKit.UnitTests/PropertyResolverTests.cs index 0d460b6..d741e84 100644 --- a/QueryKit.UnitTests/PropertyResolverTests.cs +++ b/QueryKit.UnitTests/PropertyResolverTests.cs @@ -770,6 +770,33 @@ public void property_prevented_for_filter_and_sort_is_removed_by_its_query_name_ filterExpression.ToDisplayString().Should().Be("x => (x.Age > 100)"); } + [Fact] + public void query_name_of_a_property_prevented_for_filter_and_sort_in_a_quoted_value_is_not_changed() + { + var input = """Title == "name == x" """; + var config = new QueryKitConfiguration(config => + { + config.Property(x => x.FirstName!).HasQueryName("name").PreventFilter().PreventSort(); + }); + + var filterExpression = FilterParser.ParseFilter(input, config); + + filterExpression.ToDisplayString().Should().Be("""x => (x.Title == "name == x")"""); + } + + [Fact] + public void alias_replacement_does_not_throw_for_a_property_prevented_for_filter_and_sort() + { + var config = new QueryKitConfiguration(config => + { + config.Property(x => x.Title!).HasQueryName("name").PreventFilter().PreventSort(); + }); + + var input = config.PropertyMappings.ReplaceAliasesWithPropertyPaths("""name == "x" """); + + input.Should().Be("""Title == "x" """); + } + [Theory] [InlineData("==")] [InlineData("_=")] diff --git a/QueryKit/QueryKitPropertyMappings.cs b/QueryKit/QueryKitPropertyMappings.cs index d914ca8..16ecdf8 100644 --- a/QueryKit/QueryKitPropertyMappings.cs +++ b/QueryKit/QueryKitPropertyMappings.cs @@ -153,11 +153,6 @@ public string ReplaceAliasesWithPropertyPaths(string input) // (e.g. the `^` in `^^` would otherwise be read as a start-of-line anchor). var regex = AliasRegexCache.Get($@"\b{Regex.Escape(queryKitPropertyInfo.QueryName!)}\b(?=\s*{Regex.Escape(op)})"); - if (queryKitPropertyInfo is { CanSort: false, CanFilter: false} && regex.IsMatch(input)) - { - throw new InvalidOperationException($"'{queryKitPropertyInfo.Name}' is not allowed for filtering or sorting."); - } - input = regex.Replace(input, propertyPath); } }