Skip to content

Commit 9afcc89

Browse files
committed
Replace FieldChainRequirements with pattern syntax
1 parent f1ac74b commit 9afcc89

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2739
-716
lines changed

src/JsonApiDotNetCore/CollectionExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ public static int FindIndex<T>(this IReadOnlyList<T> source, Predicate<T> match)
3232
return -1;
3333
}
3434

35+
public static IEnumerable<T> ToEnumerable<T>(this LinkedListNode<T>? startNode)
36+
{
37+
LinkedListNode<T>? current = startNode;
38+
39+
while (current != null)
40+
{
41+
yield return current.Value;
42+
43+
current = current.Next;
44+
}
45+
}
46+
3547
public static bool DictionaryEqual<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue>? first, IReadOnlyDictionary<TKey, TValue>? second,
3648
IEqualityComparer<TValue>? valueComparer = null)
3749
{

src/JsonApiDotNetCore/Queries/Internal/Parsing/FieldChainInheritanceRequirement.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/JsonApiDotNetCore/Queries/Internal/Parsing/FieldChainRequirements.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs

Lines changed: 39 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@
44
using JsonApiDotNetCore.Configuration;
55
using JsonApiDotNetCore.Queries.Expressions;
66
using JsonApiDotNetCore.QueryStrings;
7+
using JsonApiDotNetCore.QueryStrings.FieldChains;
78
using JsonApiDotNetCore.Resources;
89
using JsonApiDotNetCore.Resources.Annotations;
910
using JsonApiDotNetCore.Resources.Internal;
1011

1112
namespace JsonApiDotNetCore.Queries.Internal.Parsing;
1213

14+
/// <summary>
15+
/// Parses the JSON:API 'filter' query string parameter value.
16+
/// </summary>
1317
[PublicAPI]
1418
public class FilterParser : QueryExpressionParser
1519
{
1620
private const string PlaceholderMessageForAttributeNotString = "JsonApiDotNetCore:Placeholder_for_attribute_not_string";
1721

1822
private readonly IResourceFactory _resourceFactory;
1923
private readonly IEnumerable<IFilterValueConverter> _filterValueConverters;
20-
private ResourceType? _resourceTypeInScope;
24+
private ResourceType _resourceTypeInScope = null!;
2125

2226
public FilterParser(IResourceFactory resourceFactory, IEnumerable<IFilterValueConverter> filterValueConverters)
2327
{
@@ -28,11 +32,11 @@ public FilterParser(IResourceFactory resourceFactory, IEnumerable<IFilterValueCo
2832
_filterValueConverters = filterValueConverters;
2933
}
3034

31-
public FilterExpression Parse(string source, ResourceType resourceTypeInScope)
35+
public FilterExpression Parse(string source, ResourceType resourceType)
3236
{
33-
ArgumentGuard.NotNull(resourceTypeInScope);
37+
ArgumentGuard.NotNull(resourceType);
3438

35-
return InScopeOfResourceType(resourceTypeInScope, () =>
39+
return InScopeOfResourceType(resourceType, () =>
3640
{
3741
Tokenize(source);
3842

@@ -142,11 +146,11 @@ protected ComparisonExpression ParseComparison(string operatorName)
142146
EatSingleCharacterToken(TokenKind.OpenParen);
143147

144148
// Allow equality comparison of a to-one relationship with null.
145-
FieldChainRequirements leftChainRequirements = comparisonOperator == ComparisonOperator.Equals
146-
? FieldChainRequirements.EndsInAttribute | FieldChainRequirements.EndsInToOne
147-
: FieldChainRequirements.EndsInAttribute;
149+
FieldChainPattern leftChainPattern = comparisonOperator == ComparisonOperator.Equals
150+
? BuiltInPatterns.ToOneChainEndingInAttributeOrToOne
151+
: BuiltInPatterns.ToOneChainEndingInAttribute;
148152

149-
QueryExpression leftTerm = ParseCountOrField(leftChainRequirements);
153+
QueryExpression leftTerm = ParseCountOrField(leftChainPattern);
150154

151155
EatSingleCharacterToken(TokenKind.Comma);
152156

@@ -155,12 +159,12 @@ protected ComparisonExpression ParseComparison(string operatorName)
155159
if (leftTerm is CountExpression)
156160
{
157161
Func<string, int, object> rightConstantValueConverter = GetConstantValueConverterForCount();
158-
rightTerm = ParseCountOrConstantOrField(FieldChainRequirements.EndsInAttribute, rightConstantValueConverter);
162+
rightTerm = ParseCountOrConstantOrField(rightConstantValueConverter);
159163
}
160164
else if (leftTerm is ResourceFieldChainExpression fieldChain && fieldChain.Fields[^1] is AttrAttribute attribute)
161165
{
162166
Func<string, int, object> rightConstantValueConverter = GetConstantValueConverterForAttribute(attribute, typeof(ComparisonExpression));
163-
rightTerm = ParseCountOrConstantOrNullOrField(FieldChainRequirements.EndsInAttribute, rightConstantValueConverter);
167+
rightTerm = ParseCountOrConstantOrNullOrField(rightConstantValueConverter);
164168
}
165169
else
166170
{
@@ -179,7 +183,9 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
179183

180184
int fieldChainStartPosition = GetNextTokenPositionOrEnd();
181185

182-
ResourceFieldChainExpression targetAttributeChain = ParseFieldChain(FieldChainRequirements.EndsInAttribute, null);
186+
ResourceFieldChainExpression targetAttributeChain =
187+
ParseFieldChain(BuiltInPatterns.ToOneChainEndingInAttribute, FieldChainPatternMatchOptions.None, _resourceTypeInScope, null);
188+
183189
var targetAttribute = (AttrAttribute)targetAttributeChain.Fields[^1];
184190

185191
EatSingleCharacterToken(TokenKind.Comma);
@@ -193,7 +199,7 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
193199
}
194200
catch (QueryParseException exception) when (exception.Message == PlaceholderMessageForAttributeNotString)
195201
{
196-
int attributePosition = GetPositionOfLastField(targetAttributeChain, fieldChainStartPosition);
202+
int attributePosition = fieldChainStartPosition + GetRelativePositionOfLastFieldInChain(targetAttributeChain);
197203
throw new QueryParseException("Attribute of type 'String' expected.", attributePosition);
198204
}
199205

@@ -203,24 +209,14 @@ protected MatchTextExpression ParseTextMatch(string matchFunctionName)
203209
return new MatchTextExpression(targetAttributeChain, constant, matchKind);
204210
}
205211

206-
private static int GetPositionOfLastField(ResourceFieldChainExpression fieldChain, int startPosition)
207-
{
208-
int position = 0;
209-
210-
for (int index = 0; index < fieldChain.Fields.Count - 1; index++)
211-
{
212-
position += fieldChain.Fields[index].PublicName.Length + 1;
213-
}
214-
215-
return startPosition + position;
216-
}
217-
218212
protected AnyExpression ParseAny()
219213
{
220214
EatText(Keywords.Any);
221215
EatSingleCharacterToken(TokenKind.OpenParen);
222216

223-
ResourceFieldChainExpression targetAttributeChain = ParseFieldChain(FieldChainRequirements.EndsInAttribute, null);
217+
ResourceFieldChainExpression targetAttributeChain =
218+
ParseFieldChain(BuiltInPatterns.ToOneChainEndingInAttribute, FieldChainPatternMatchOptions.None, _resourceTypeInScope, null);
219+
224220
var targetAttribute = (AttrAttribute)targetAttributeChain.Fields[^1];
225221

226222
EatSingleCharacterToken(TokenKind.Comma);
@@ -251,7 +247,9 @@ protected HasExpression ParseHas()
251247
EatText(Keywords.Has);
252248
EatSingleCharacterToken(TokenKind.OpenParen);
253249

254-
ResourceFieldChainExpression targetCollection = ParseFieldChain(FieldChainRequirements.EndsInToMany, null);
250+
ResourceFieldChainExpression targetCollection =
251+
ParseFieldChain(BuiltInPatterns.ToOneChainEndingInToMany, FieldChainPatternMatchOptions.None, _resourceTypeInScope, null);
252+
255253
FilterExpression? filter = null;
256254

257255
if (TokenStack.TryPeek(out Token? nextToken) && nextToken.Kind == TokenKind.Comma)
@@ -280,7 +278,7 @@ private IsTypeExpression ParseIsType()
280278

281279
EatSingleCharacterToken(TokenKind.Comma);
282280

283-
ResourceType baseType = targetToOneRelationship != null ? ((RelationshipAttribute)targetToOneRelationship.Fields[^1]).RightType : _resourceTypeInScope!;
281+
ResourceType baseType = targetToOneRelationship != null ? ((RelationshipAttribute)targetToOneRelationship.Fields[^1]).RightType : _resourceTypeInScope;
284282
ResourceType derivedType = ParseDerivedType(baseType);
285283

286284
FilterExpression? child = TryParseFilterInIsType(derivedType);
@@ -297,7 +295,7 @@ private IsTypeExpression ParseIsType()
297295
return null;
298296
}
299297

300-
return ParseFieldChain(FieldChainRequirements.EndsInToOne, "Relationship name or , expected.");
298+
return ParseFieldChain(BuiltInPatterns.ToOneChain, FieldChainPatternMatchOptions.None, _resourceTypeInScope, "Relationship name or , expected.");
301299
}
302300

303301
private ResourceType ParseDerivedType(ResourceType baseType)
@@ -359,21 +357,21 @@ private ResourceType ResolveDerivedType(ResourceType baseType, string derivedTyp
359357
return filter;
360358
}
361359

362-
protected QueryExpression ParseCountOrField(FieldChainRequirements chainRequirements)
360+
protected QueryExpression ParseCountOrField(FieldChainPattern pattern)
363361
{
364-
CountExpression? count = TryParseCount();
362+
CountExpression? count = TryParseCount(FieldChainPatternMatchOptions.None, _resourceTypeInScope);
365363

366364
if (count != null)
367365
{
368366
return count;
369367
}
370368

371-
return ParseFieldChain(chainRequirements, "Count function or field name expected.");
369+
return ParseFieldChain(pattern, FieldChainPatternMatchOptions.None, _resourceTypeInScope, "Count function or field name expected.");
372370
}
373371

374-
protected QueryExpression ParseCountOrConstantOrField(FieldChainRequirements chainRequirements, Func<string, int, object> constantValueConverter)
372+
protected QueryExpression ParseCountOrConstantOrField(Func<string, int, object> constantValueConverter)
375373
{
376-
CountExpression? count = TryParseCount();
374+
CountExpression? count = TryParseCount(FieldChainPatternMatchOptions.None, _resourceTypeInScope);
377375

378376
if (count != null)
379377
{
@@ -387,12 +385,13 @@ protected QueryExpression ParseCountOrConstantOrField(FieldChainRequirements cha
387385
return constant;
388386
}
389387

390-
return ParseFieldChain(chainRequirements, "Count function, value between quotes or field name expected.");
388+
return ParseFieldChain(BuiltInPatterns.ToOneChainEndingInAttribute, FieldChainPatternMatchOptions.None, _resourceTypeInScope,
389+
"Count function, value between quotes or field name expected.");
391390
}
392391

393-
protected QueryExpression ParseCountOrConstantOrNullOrField(FieldChainRequirements chainRequirements, Func<string, int, object> constantValueConverter)
392+
protected QueryExpression ParseCountOrConstantOrNullOrField(Func<string, int, object> constantValueConverter)
394393
{
395-
CountExpression? count = TryParseCount();
394+
CountExpression? count = TryParseCount(FieldChainPatternMatchOptions.None, _resourceTypeInScope);
396395

397396
if (count != null)
398397
{
@@ -406,7 +405,8 @@ protected QueryExpression ParseCountOrConstantOrNullOrField(FieldChainRequiremen
406405
return constantOrNull;
407406
}
408407

409-
return ParseFieldChain(chainRequirements, "Count function, value between quotes, null or field name expected.");
408+
return ParseFieldChain(BuiltInPatterns.ToOneChainEndingInAttribute, FieldChainPatternMatchOptions.None, _resourceTypeInScope,
409+
"Count function, value between quotes, null or field name expected.");
410410
}
411411

412412
protected LiteralConstantExpression? TryParseConstant(Func<string, int, object> constantValueConverter)
@@ -546,34 +546,7 @@ private object DeObfuscateStringId(Type resourceClrType, string stringId)
546546
return tempResource.GetTypedId();
547547
}
548548

549-
protected override IImmutableList<ResourceFieldAttribute> OnResolveFieldChain(string path, int position, FieldChainRequirements chainRequirements)
550-
{
551-
if (chainRequirements == FieldChainRequirements.EndsInToMany)
552-
{
553-
return ChainResolver.ResolveToOneChainEndingInToMany(_resourceTypeInScope!, path, position, FieldChainInheritanceRequirement.Disabled,
554-
ValidateSingleField);
555-
}
556-
557-
if (chainRequirements == FieldChainRequirements.EndsInAttribute)
558-
{
559-
return ChainResolver.ResolveToOneChainEndingInAttribute(_resourceTypeInScope!, path, position, FieldChainInheritanceRequirement.Disabled,
560-
ValidateSingleField);
561-
}
562-
563-
if (chainRequirements == FieldChainRequirements.EndsInToOne)
564-
{
565-
return ChainResolver.ResolveToOneChain(_resourceTypeInScope!, path, position, ValidateSingleField);
566-
}
567-
568-
if (chainRequirements.HasFlag(FieldChainRequirements.EndsInAttribute) && chainRequirements.HasFlag(FieldChainRequirements.EndsInToOne))
569-
{
570-
return ChainResolver.ResolveToOneChainEndingInAttributeOrToOne(_resourceTypeInScope!, path, position, ValidateSingleField);
571-
}
572-
573-
throw new InvalidOperationException($"Unexpected combination of chain requirement flags '{chainRequirements}'.");
574-
}
575-
576-
protected override void ValidateSingleField(ResourceFieldAttribute field, ResourceType resourceType, int position)
549+
protected override void ValidateField(ResourceFieldAttribute field, int position)
577550
{
578551
if (field.IsFilterBlocked())
579552
{
@@ -584,7 +557,7 @@ protected override void ValidateSingleField(ResourceFieldAttribute field, Resour
584557

585558
private TResult InScopeOfResourceType<TResult>(ResourceType resourceType, Func<TResult> action)
586559
{
587-
ResourceType? backupType = _resourceTypeInScope;
560+
ResourceType backupType = _resourceTypeInScope;
588561

589562
try
590563
{

0 commit comments

Comments
 (0)