Skip to content

Commit 8af8219

Browse files
author
Bart Koelman
committed
Optimized check for maximum include depth (single pass).
1 parent 5d625fa commit 8af8219

File tree

3 files changed

+22
-30
lines changed

3 files changed

+22
-30
lines changed

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ public IncludeParser(IResourceContextProvider resourceContextProvider,
1919
_validateSingleRelationshipCallback = validateSingleRelationshipCallback;
2020
}
2121

22-
public IncludeExpression Parse(string source, ResourceContext resourceContextInScope)
22+
public IncludeExpression Parse(string source, ResourceContext resourceContextInScope, int? maximumDepth)
2323
{
2424
_resourceContextInScope = resourceContextInScope ?? throw new ArgumentNullException(nameof(resourceContextInScope));
2525
Tokenize(source);
2626

27-
var expression = ParseInclude();
27+
var expression = ParseInclude(maximumDepth);
2828

2929
AssertTokenStackIsEmpty();
3030

3131
return expression;
3232
}
3333

34-
protected IncludeExpression ParseInclude()
34+
protected IncludeExpression ParseInclude(int? maximumDepth)
3535
{
3636
ResourceFieldChainExpression firstChain =
3737
ParseFieldChain(FieldChainRequirements.IsRelationship, "Relationship name expected.");
@@ -49,9 +49,26 @@ protected IncludeExpression ParseInclude()
4949
chains.Add(nextChain);
5050
}
5151

52+
ValidateMaximumIncludeDepth(maximumDepth, chains);
53+
5254
return IncludeChainConverter.FromRelationshipChains(chains);
5355
}
5456

57+
private static void ValidateMaximumIncludeDepth(int? maximumDepth, List<ResourceFieldChainExpression> chains)
58+
{
59+
if (maximumDepth != null)
60+
{
61+
foreach (var chain in chains)
62+
{
63+
if (chain.Fields.Count > maximumDepth)
64+
{
65+
var path = string.Join('.', chain.Fields.Select(field => field.PublicName));
66+
throw new QueryParseException($"Including '{path}' exceeds the maximum inclusion depth of {maximumDepth}.");
67+
}
68+
}
69+
}
70+
}
71+
5572
protected override IReadOnlyCollection<ResourceFieldAttribute> OnResolveFieldChain(string path, FieldChainRequirements chainRequirements)
5673
{
5774
return ChainResolver.ResolveRelationshipChain(_resourceContextInScope, path, _validateSingleRelationshipCallback);

src/JsonApiDotNetCore/Internal/QueryStrings/IncludeQueryStringParameterReader.cs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using JsonApiDotNetCore.Configuration;
54
using JsonApiDotNetCore.Controllers;
65
using JsonApiDotNetCore.Exceptions;
@@ -75,31 +74,7 @@ public void Read(string parameterName, StringValues parameterValue)
7574

7675
private IncludeExpression GetInclude(string parameterValue)
7776
{
78-
IncludeExpression include = _includeParser.Parse(parameterValue, RequestResource);
79-
80-
ValidateMaximumIncludeDepth(include);
81-
82-
return include;
83-
}
84-
85-
private void ValidateMaximumIncludeDepth(IncludeExpression include)
86-
{
87-
if (_options.MaximumIncludeDepth != null)
88-
{
89-
var chains = IncludeChainConverter.GetRelationshipChains(include);
90-
91-
foreach (var chain in chains)
92-
{
93-
if (chain.Fields.Count > _options.MaximumIncludeDepth)
94-
{
95-
var path = string.Join('.', chain.Fields.Select(field => field.PublicName));
96-
97-
throw new InvalidQueryStringParameterException(_lastParameterName,
98-
"Including at the requested depth is not allowed.",
99-
$"Including '{path}' exceeds the maximum inclusion depth of {_options.MaximumIncludeDepth}.");
100-
}
101-
}
102-
}
77+
return _includeParser.Parse(parameterValue, RequestResource, _options.MaximumIncludeDepth);
10378
}
10479

10580
public IReadOnlyCollection<ExpressionInScope> GetConstraints()

test/JsonApiDotNetCoreExampleTests/IntegrationTests/Includes/IncludeTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ public async Task Cannot_exceed_configured_maximum_inclusion_depth()
833833

834834
responseDocument.Errors.Should().HaveCount(1);
835835
responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.BadRequest);
836-
responseDocument.Errors[0].Title.Should().Be("Including at the requested depth is not allowed.");
836+
responseDocument.Errors[0].Title.Should().Be("The specified include is invalid.");
837837
responseDocument.Errors[0].Detail.Should().Be("Including 'articles.revisions' exceeds the maximum inclusion depth of 1.");
838838
responseDocument.Errors[0].Source.Parameter.Should().Be("include");
839839
}

0 commit comments

Comments
 (0)