Skip to content

Commit df67dae

Browse files
authored
fix(filter)!: apply PreventFilter and PreventSort to every property path (#136)
* fix(filter)!: apply PreventFilter and PreventSort to every property path PreventFilter was checked only for a left-side member, by its name in the exact case after the query-name rewrite. Arithmetic, the right side of a comparison, a property list in another case, derived properties, and custom operations skipped the check. PreventSort was checked by the typed path in the exact case. A caller could learn the value of a hidden field one comparison at a time. The parser now resolves each property reference and applies the prevent settings in each of these places. A prevented clause follows IgnoredClauseBehavior, and a prevented sort is skipped. Arithmetic does not apply MaxPropertyDepth in this change. BREAKING CHANGE: a filter or sort that reaches a property with PreventFilter or PreventSort through arithmetic, the right side, a property list, another letter case, the member name of a property with a query name, a derived property, or a custom operation no longer filters or sorts by that property. The clause follows IgnoredClauseBehavior, and the sort is skipped. * fix(filter)!: build the right side and the sort from the resolved path
1 parent b67a8bc commit df67dae

7 files changed

Lines changed: 379 additions & 48 deletions

File tree

‎QueryKit.IntegrationTests/Tests/PropertyResolverTests.cs‎

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,214 @@ public async Task property_is_not_prevented_by_another_property_whose_query_name
150150
people[0].Id.Should().Be(fakePerson.Id);
151151
}
152152

153+
[Fact]
154+
public async Task prevented_property_in_arithmetic_is_not_filtered()
155+
{
156+
// Arrange
157+
var testingServiceScope = new TestingServiceScope();
158+
var title = new Faker().Lorem.Sentence();
159+
var fakePerson = new FakeTestingPersonBuilder()
160+
.WithTitle(title)
161+
.WithAge(5)
162+
.Build();
163+
await testingServiceScope.InsertAsync(fakePerson);
164+
165+
var input = $"""Title == "{title}" && (Age + 0) > 10""";
166+
var config = new QueryKitConfiguration(config =>
167+
{
168+
config.Property<TestingPerson>(x => x.Age!).PreventFilter();
169+
});
170+
171+
// Act
172+
var people = await testingServiceScope.DbContext().People
173+
.ApplyQueryKitFilter(input, config)
174+
.ToListAsync();
175+
176+
// Assert
177+
people.Should().ContainSingle(x => x.Id == fakePerson.Id);
178+
}
179+
180+
[Fact]
181+
public async Task prevented_property_on_the_right_side_is_not_compared()
182+
{
183+
// Arrange
184+
var testingServiceScope = new TestingServiceScope();
185+
var title = new Faker().Lorem.Sentence();
186+
var fakePerson = new FakeTestingPersonBuilder()
187+
.WithTitle(title)
188+
.WithFirstName("Same")
189+
.WithLastName("Same")
190+
.WithAge(30)
191+
.Build();
192+
await testingServiceScope.InsertAsync(fakePerson);
193+
194+
var input = $"""Title == "{title}" && (FirstName == LastName || Age > 100)""";
195+
var config = new QueryKitConfiguration(config =>
196+
{
197+
config.IgnoredClauseBehavior = IgnoredClauseBehavior.Remove;
198+
config.Property<TestingPerson>(x => x.LastName!).PreventFilter();
199+
});
200+
201+
// Act
202+
var people = await testingServiceScope.DbContext().People
203+
.ApplyQueryKitFilter(input, config)
204+
.ToListAsync();
205+
206+
// Assert
207+
people.Should().BeEmpty();
208+
}
209+
210+
[Fact]
211+
public async Task prevented_property_in_a_list_is_not_filtered_in_any_case()
212+
{
213+
// Arrange
214+
var testingServiceScope = new TestingServiceScope();
215+
var title = new Faker().Lorem.Sentence();
216+
var fakePerson = new FakeTestingPersonBuilder()
217+
.WithTitle(title)
218+
.WithFirstName("Paul")
219+
.WithLastName("Other")
220+
.Build();
221+
await testingServiceScope.InsertAsync(fakePerson);
222+
223+
var input = $"""Title == "{title}" && (firstname, LastName) == "Paul" """;
224+
var config = new QueryKitConfiguration(config =>
225+
{
226+
config.Property<TestingPerson>(x => x.FirstName!).PreventFilter();
227+
});
228+
229+
// Act
230+
var people = await testingServiceScope.DbContext().People
231+
.ApplyQueryKitFilter(input, config)
232+
.ToListAsync();
233+
234+
// Assert
235+
people.Should().BeEmpty();
236+
}
237+
238+
[Fact]
239+
public async Task prevented_sort_property_with_a_query_name_is_not_sorted_when_written_by_its_member_name()
240+
{
241+
// Arrange
242+
var testingServiceScope = new TestingServiceScope();
243+
var title = new Faker().Lorem.Sentence();
244+
var firstPerson = new FakeTestingPersonBuilder()
245+
.WithTitle(title)
246+
.WithFirstName("A")
247+
.WithAge(1)
248+
.Build();
249+
var secondPerson = new FakeTestingPersonBuilder()
250+
.WithTitle(title)
251+
.WithFirstName("B")
252+
.WithAge(2)
253+
.Build();
254+
await testingServiceScope.InsertAsync(firstPerson, secondPerson);
255+
256+
var input = "firstname desc, Age";
257+
var config = new QueryKitConfiguration(config =>
258+
{
259+
config.Property<TestingPerson>(x => x.FirstName!).HasQueryName("first").PreventSort();
260+
});
261+
262+
// Act
263+
var people = await testingServiceScope.DbContext().People
264+
.Where(x => x.Title == title)
265+
.ApplyQueryKitSort(input, config)
266+
.ToListAsync();
267+
268+
// Assert
269+
people.Select(x => x.Id).Should().Equal(firstPerson.Id, secondPerson.Id);
270+
}
271+
272+
[Fact]
273+
public async Task prevented_custom_operation_is_not_filtered()
274+
{
275+
// Arrange
276+
var testingServiceScope = new TestingServiceScope();
277+
var title = new Faker().Lorem.Sentence();
278+
var fakePerson = new FakeTestingPersonBuilder()
279+
.WithTitle(title)
280+
.WithAge(5)
281+
.Build();
282+
await testingServiceScope.InsertAsync(fakePerson);
283+
284+
var input = $"""Title == "{title}" && adult == true""";
285+
var config = new QueryKitConfiguration(config =>
286+
{
287+
config.CustomOperation<TestingPerson>((x, op, value) => x.Age > 17).HasQueryName("adult").PreventFilter();
288+
});
289+
290+
// Act
291+
var people = await testingServiceScope.DbContext().People
292+
.ApplyQueryKitFilter(input, config)
293+
.ToListAsync();
294+
295+
// Assert
296+
people.Should().ContainSingle(x => x.Id == fakePerson.Id);
297+
}
298+
299+
[Fact]
300+
public async Task prevented_derived_property_is_not_filtered()
301+
{
302+
// Arrange
303+
var testingServiceScope = new TestingServiceScope();
304+
var title = new Faker().Lorem.Sentence();
305+
var fakePerson = new FakeTestingPersonBuilder()
306+
.WithTitle(title)
307+
.WithFirstName("Paul")
308+
.WithLastName("Other")
309+
.Build();
310+
await testingServiceScope.InsertAsync(fakePerson);
311+
312+
var input = $"""Title == "{title}" && full == "no match" """;
313+
var config = new QueryKitConfiguration(config =>
314+
{
315+
config.DerivedProperty<TestingPerson>(x => x.FirstName + " " + x.LastName).HasQueryName("full").PreventFilter();
316+
});
317+
318+
// Act
319+
var people = await testingServiceScope.DbContext().People
320+
.ApplyQueryKitFilter(input, config)
321+
.ToListAsync();
322+
323+
// Assert
324+
people.Should().ContainSingle(x => x.Id == fakePerson.Id);
325+
}
326+
327+
[Fact]
328+
public async Task prevented_derived_sort_property_is_not_sorted()
329+
{
330+
// Arrange
331+
var testingServiceScope = new TestingServiceScope();
332+
var title = new Faker().Lorem.Sentence();
333+
var firstPerson = new FakeTestingPersonBuilder()
334+
.WithTitle(title)
335+
.WithFirstName("A")
336+
.WithAge(1)
337+
.Build();
338+
var secondPerson = new FakeTestingPersonBuilder()
339+
.WithTitle(title)
340+
.WithFirstName("B")
341+
.WithAge(2)
342+
.Build();
343+
await testingServiceScope.InsertAsync(firstPerson, secondPerson);
344+
345+
var input = "full desc, Age";
346+
var config = new QueryKitConfiguration(config =>
347+
{
348+
config.DerivedProperty<TestingPerson>(x => x.FirstName + " " + x.LastName).HasQueryName("full").PreventSort();
349+
});
350+
351+
// Act
352+
var people = await testingServiceScope.DbContext().People
353+
.Where(x => x.Title == title)
354+
.ApplyQueryKitSort(input, config)
355+
.ToListAsync();
356+
357+
// Assert
358+
people.Select(x => x.Id).Should().Equal(firstPerson.Id, secondPerson.Id);
359+
}
360+
153361
[Theory]
154362
[InlineData("first-name")]
155363
[InlineData("_first")]

0 commit comments

Comments
 (0)