mirror of
https://github.com/Biarity/Sieve.git
synced 2025-09-19 06:39:43 +02:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7b6f3c7d85 | ||
|
5ef8843f3d |
3
.github/workflows/ci_publish.yml
vendored
3
.github/workflows/ci_publish.yml
vendored
@@ -19,8 +19,9 @@ name: ci_publish
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
|
||||||
- 'releases/*'
|
- 'releases/*'
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ubuntu-latest:
|
ubuntu-latest:
|
||||||
|
@@ -6,43 +6,36 @@ namespace Sieve.Models
|
|||||||
{
|
{
|
||||||
public class FilterTerm : IFilterTerm, IEquatable<FilterTerm>
|
public class FilterTerm : IFilterTerm, IEquatable<FilterTerm>
|
||||||
{
|
{
|
||||||
public FilterTerm() { }
|
private const string EscapedPipePattern = @"(?<!($|[^\\]|^)(\\\\)*?\\)\|";
|
||||||
|
|
||||||
private const string EscapedPipePattern = @"(?<!($|[^\\])(\\\\)*?\\)\|";
|
|
||||||
private const string PipeToEscape = @"\|";
|
private const string PipeToEscape = @"\|";
|
||||||
|
private const string BackslashToEscape = @"\\";
|
||||||
private static readonly string[] Operators = new string[] {
|
private const string OperatorsRegEx = @"(!@=\*|!_=\*|!=\*|!@=|!_=|==\*|@=\*|_=\*|==|!=|>=|<=|>|<|@=|_=)";
|
||||||
"!@=*",
|
private const string EscapeNegPatternForOper = @"(?<!\\)" + OperatorsRegEx;
|
||||||
"!_=*",
|
private const string EscapePosPatternForOper = @"(?<=\\)" + OperatorsRegEx;
|
||||||
"!=*",
|
|
||||||
"!@=",
|
|
||||||
"!_=",
|
|
||||||
"==*",
|
|
||||||
"@=*",
|
|
||||||
"_=*",
|
|
||||||
"==",
|
|
||||||
"!=",
|
|
||||||
">=",
|
|
||||||
"<=",
|
|
||||||
">",
|
|
||||||
"<",
|
|
||||||
"@=",
|
|
||||||
"_="
|
|
||||||
};
|
|
||||||
|
|
||||||
public string Filter
|
public string Filter
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
var filterSplits = value.Split(Operators, StringSplitOptions.RemoveEmptyEntries)
|
var filterSplits = Regex.Split(value,EscapeNegPatternForOper).Select(t => t.Trim()).ToArray();
|
||||||
.Select(t => t.Trim()).ToArray();
|
|
||||||
Names = Regex.Split(filterSplits[0], EscapedPipePattern).Select(t => t.Trim()).ToArray();
|
Names = Regex.Split(filterSplits[0], EscapedPipePattern).Select(t => t.Trim()).ToArray();
|
||||||
Values = filterSplits.Length > 1
|
|
||||||
? Regex.Split(filterSplits[1], EscapedPipePattern)
|
if (filterSplits.Length > 2)
|
||||||
|
{
|
||||||
|
foreach (var match in Regex.Matches(filterSplits[2],EscapePosPatternForOper))
|
||||||
|
{
|
||||||
|
var matchStr = match.ToString();
|
||||||
|
filterSplits[2] = filterSplits[2].Replace('\\' + matchStr, matchStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Values = Regex.Split(filterSplits[2], EscapedPipePattern)
|
||||||
.Select(t => t.Replace(PipeToEscape, "|").Trim())
|
.Select(t => t.Replace(PipeToEscape, "|").Trim())
|
||||||
.ToArray()
|
.Select(t => t.Replace(BackslashToEscape, "\\").Trim())
|
||||||
: null;
|
.ToArray();
|
||||||
Operator = Array.Find(Operators, o => value.Contains(o)) ?? "==";
|
}
|
||||||
|
|
||||||
|
Operator = Regex.Match(value,EscapeNegPatternForOper).Value;
|
||||||
OperatorParsed = GetOperatorParsed(Operator);
|
OperatorParsed = GetOperatorParsed(Operator);
|
||||||
OperatorIsCaseInsensitive = Operator.EndsWith("*");
|
OperatorIsCaseInsensitive = Operator.EndsWith("*");
|
||||||
OperatorIsNegated = OperatorParsed != FilterOperator.NotEquals && Operator.StartsWith("!");
|
OperatorIsNegated = OperatorParsed != FilterOperator.NotEquals && Operator.StartsWith("!");
|
||||||
|
@@ -73,7 +73,7 @@ namespace SieveUnitTests
|
|||||||
CategoryId = 2,
|
CategoryId = 2,
|
||||||
TopComment = new Comment { Id = 1, Text = "D1" },
|
TopComment = new Comment { Id = 1, Text = "D1" },
|
||||||
FeaturedComment = new Comment { Id = 7, Text = "D2" }
|
FeaturedComment = new Comment { Id = 7, Text = "D2" }
|
||||||
},
|
}
|
||||||
}.AsQueryable();
|
}.AsQueryable();
|
||||||
|
|
||||||
_comments = new List<Comment>
|
_comments = new List<Comment>
|
||||||
@@ -684,15 +684,116 @@ namespace SieveUnitTests
|
|||||||
DateCreated = DateTimeOffset.UtcNow.AddDays(-1),
|
DateCreated = DateTimeOffset.UtcNow.AddDays(-1),
|
||||||
Text = "Here is | another comment"
|
Text = "Here is | another comment"
|
||||||
},
|
},
|
||||||
|
new Comment
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
DateCreated = DateTimeOffset.UtcNow.AddDays(-1),
|
||||||
|
Text = @"Here is \| another comment(1)"
|
||||||
|
}
|
||||||
}.AsQueryable();
|
}.AsQueryable();
|
||||||
|
|
||||||
var model = new SieveModel()
|
var model = new SieveModel
|
||||||
{
|
{
|
||||||
Filters = "Text==Here is \\| a comment|Here is \\| another comment",
|
Filters = @"Text==Here is \| a comment|Here is \| another comment|Here is \\\| another comment(1)",
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = _processor.Apply(model, comments);
|
var result = _processor.Apply(model, comments);
|
||||||
Assert.Equal(2, result.Count());
|
Assert.Equal(3, result.Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("CategoryId==1,(CategoryId|LikeCount)==50")]
|
||||||
|
[InlineData("(CategoryId|LikeCount)==50,CategoryId==1")]
|
||||||
|
public void CanFilterWithEscape(string filter)
|
||||||
|
{
|
||||||
|
var model = new SieveModel
|
||||||
|
{
|
||||||
|
Filters = filter
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _processor.Apply(model, _posts);
|
||||||
|
var entry = result.FirstOrDefault();
|
||||||
|
var resultCount = result.Count();
|
||||||
|
|
||||||
|
Assert.NotNull(entry);
|
||||||
|
Assert.Equal(1, resultCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(@"Title@=\\")]
|
||||||
|
public void CanFilterWithEscapedBackSlash(string filter)
|
||||||
|
{
|
||||||
|
var posts = new List<Post>
|
||||||
|
{
|
||||||
|
new Post
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Title = "E\\",
|
||||||
|
LikeCount = 4,
|
||||||
|
IsDraft = true,
|
||||||
|
CategoryId = 1,
|
||||||
|
TopComment = new Comment { Id = 1, Text = "E1" },
|
||||||
|
FeaturedComment = new Comment { Id = 7, Text = "E2" }
|
||||||
|
}
|
||||||
|
}.AsQueryable();
|
||||||
|
|
||||||
|
var model = new SieveModel
|
||||||
|
{
|
||||||
|
Filters = filter
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _processor.Apply(model, posts);
|
||||||
|
var entry = result.FirstOrDefault();
|
||||||
|
var resultCount = result.Count();
|
||||||
|
|
||||||
|
Assert.NotNull(entry);
|
||||||
|
Assert.Equal(1, resultCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(@"Title@=\== ")]
|
||||||
|
[InlineData(@"Title@=\!= ")]
|
||||||
|
[InlineData(@"Title@=\> ")]
|
||||||
|
[InlineData(@"Title@=\< ")]
|
||||||
|
[InlineData(@"Title@=\<= ")]
|
||||||
|
[InlineData(@"Title@=\>= ")]
|
||||||
|
[InlineData(@"Title@=\@= ")]
|
||||||
|
[InlineData(@"Title@=\_= ")]
|
||||||
|
[InlineData(@"Title@=!\@= ")]
|
||||||
|
[InlineData(@"Title@=!\_= ")]
|
||||||
|
[InlineData(@"Title@=\@=* ")]
|
||||||
|
[InlineData(@"Title@=\_=* ")]
|
||||||
|
[InlineData(@"Title@=\==* ")]
|
||||||
|
[InlineData(@"Title@=\!=* ")]
|
||||||
|
[InlineData(@"Title@=!\@=* ")]
|
||||||
|
public void CanFilterWithEscapedOperators(string filter)
|
||||||
|
{
|
||||||
|
var posts = new List<Post>
|
||||||
|
{
|
||||||
|
new Post
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Title = @"Operators: == != > < >= <= @= _= !@= !_= @=* _=* ==* !=* !@=* !_=* ",
|
||||||
|
LikeCount = 1,
|
||||||
|
IsDraft = true,
|
||||||
|
CategoryId = 1,
|
||||||
|
TopComment = new Comment { Id = 1, Text = "F1" },
|
||||||
|
FeaturedComment = new Comment { Id = 7, Text = "F2" }
|
||||||
|
}
|
||||||
|
}.AsQueryable();
|
||||||
|
|
||||||
|
var model = new SieveModel
|
||||||
|
{
|
||||||
|
Filters = filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _processor.Apply(model, posts);
|
||||||
|
var entry = result.FirstOrDefault();
|
||||||
|
var resultCount = result.Count();
|
||||||
|
|
||||||
|
Assert.NotNull(entry);
|
||||||
|
Assert.Equal(1, resultCount);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,8 @@ using static Nuke.Common.Tools.DotNet.DotNetTasks;
|
|||||||
InvokedTargets = new[] {nameof(Ci)},
|
InvokedTargets = new[] {nameof(Ci)},
|
||||||
CacheKeyFiles = new string[0])]
|
CacheKeyFiles = new string[0])]
|
||||||
[GitHubActions("ci_publish", GitHubActionsImage.UbuntuLatest,
|
[GitHubActions("ci_publish", GitHubActionsImage.UbuntuLatest,
|
||||||
OnPushBranches = new[] {"master", "releases/*"},
|
OnPushBranches = new[] { "releases/*" },
|
||||||
|
OnPushTags = new[] { "v*" },
|
||||||
AutoGenerate = true,
|
AutoGenerate = true,
|
||||||
InvokedTargets = new[] {nameof(CiPublish)},
|
InvokedTargets = new[] {nameof(CiPublish)},
|
||||||
CacheKeyFiles = new string[0],
|
CacheKeyFiles = new string[0],
|
||||||
@@ -83,6 +84,7 @@ class Build : NukeBuild
|
|||||||
Target Package => _ => _
|
Target Package => _ => _
|
||||||
.DependsOn(Test)
|
.DependsOn(Test)
|
||||||
.Executes(() =>
|
.Executes(() =>
|
||||||
|
|
||||||
{
|
{
|
||||||
DotNetPack(s => s
|
DotNetPack(s => s
|
||||||
.SetProject(SieveProject)
|
.SetProject(SieveProject)
|
||||||
|
Reference in New Issue
Block a user