Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions JsonApiToolkit.Tests/Extensions/IncludeFilterParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,7 @@ public void SeparateIncludeFilters_WithKebabCaseInclude_HandlesCorrectly()
var includePaths = new List<string> { "cve-comments" };

// Act
var (mainFilters, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(
filters,
includePaths
);
var (_, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(filters, includePaths);

// Assert
Assert.Single(includeFilters);
Expand Down Expand Up @@ -197,10 +194,7 @@ public void SeparateIncludeFilters_WithNestedIncludeFilter_SeparatesCorrectly()
var includePaths = new List<string> { "comments.author" };

// Act
var (mainFilters, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(
filters,
includePaths
);
var (_, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(filters, includePaths);

// Assert
Assert.Single(includeFilters);
Expand Down Expand Up @@ -237,10 +231,7 @@ public void SeparateIncludeFilters_WithComplexOrFilter_HandlesCorrectly()
var includePaths = new List<string> { "comments" };

// Act
var (mainFilters, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(
filters,
includePaths
);
var (_, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(filters, includePaths);

// Assert
Assert.Single(includeFilters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,7 @@ public void ApplySorting_WithMultipleSorts_SortsCorrectly()
.ToList()
.Select(e =>
{
if (e.Id == 1 || e.Id == 3)
e.IsActive = true;
else
e.IsActive = false;
e.IsActive = e.Id == 1 || e.Id == 3;
return e;
})
.AsQueryable();
Expand Down Expand Up @@ -314,10 +311,6 @@ public async Task CreatePaginationMetaAsync_WithInvalidPageNumber_ReturnsLastPag
{
var query = GetTestData();
var pagination = new PaginationParameters { Number = 10, Size = 2 };
var totalCount = query.Count();
var totalPages = (int)Math.Ceiling(totalCount / (double)pagination.Size);
var expectedCurrentPage = Math.Min(Math.Max(pagination.Number, 1), Math.Max(totalPages, 1));

var meta = await query.CreatePaginationMetaAsync(pagination);

Assert.Equal(5, meta.TotalResources);
Expand Down
11 changes: 3 additions & 8 deletions JsonApiToolkit.Tests/Integration/JsonApiQueryAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,20 +506,15 @@ public async Task GetArticles_WithPagination_ReturnsMetadataAsync()
Assert.Equal(2, GetPaginationValue<int>(document.Meta, "pageSize"));
}

private static T GetPaginationValue<T>(Dictionary<string, object> meta, string key)
private static int GetPaginationValue<T>(Dictionary<string, object> meta, string key)
{
if (
meta.TryGetValue("pagination", out var pagination)
&& pagination is JsonElement paginationElement
&& paginationElement.TryGetProperty(key, out var property)
)
{
if (paginationElement.TryGetProperty(key, out var property))
{
if (typeof(T) == typeof(int))
return (T)(object)property.GetInt32();
if (typeof(T) == typeof(string))
return (T)(object)property.GetString()!;
}
return property.GetInt32();
}

throw new InvalidOperationException(
Expand Down
22 changes: 11 additions & 11 deletions JsonApiToolkit.Tests/Mapping/JsonApiMapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ public void ToResourceObject_IncludesForeignKeyIdsInAttributes()

// Foreign key IDs should be included in attributes
Assert.NotNull(resourceObject.Attributes);
Assert.True(resourceObject.Attributes.ContainsKey("relatedEntityId"));
Assert.Equal(42, resourceObject.Attributes["relatedEntityId"]);
Assert.True(
resourceObject.Attributes.TryGetValue("relatedEntityId", out var relatedEntityIdValue)
);
Assert.Equal(42, relatedEntityIdValue);
}

[Fact]
Expand All @@ -70,11 +72,11 @@ public void ToResourceObject_WithRelationships_MapsRelationshipsCorrectly()
);

Assert.NotNull(resourceObject.Relationships);
Assert.True(resourceObject.Relationships.ContainsKey("relatedEntity"));

Relationship relationship = resourceObject.Relationships["relatedEntity"];
Assert.True(
resourceObject.Relationships.TryGetValue("relatedEntity", out var relationship)
);
ResourceIdentifier resourceIdentifier = Assert.IsType<ResourceIdentifier>(
relationship.Data
relationship!.Data
);
Assert.Equal("2", resourceIdentifier.Id);
Assert.Equal("testRelatedEntity", resourceIdentifier.Type);
Expand All @@ -93,9 +95,7 @@ public void ToResourceObject_WithCollectionRelationship_MapsCollectionCorrectly(
var resourceObject = JsonApiMapper.ToResourceObject(entity, "testEntities", ["Children"]);

Assert.NotNull(resourceObject.Relationships);
Assert.True(resourceObject.Relationships.ContainsKey("children"));

Relationship relationship = resourceObject.Relationships["children"];
Assert.True(resourceObject.Relationships.TryGetValue("children", out var relationship));
IEnumerable<ResourceIdentifier> identifiers = Assert.IsAssignableFrom<
IEnumerable<ResourceIdentifier>
>(relationship.Data);
Expand Down Expand Up @@ -365,8 +365,8 @@ public void ToResourceObject_ExcludesJsonIgnoreProperties()
var resourceObject = JsonApiMapper.ToResourceObject(entity, "entities");

Assert.NotNull(resourceObject.Attributes);
Assert.True(resourceObject.Attributes.ContainsKey("visibleName"));
Assert.Equal("Visible", resourceObject.Attributes["visibleName"]);
Assert.True(resourceObject.Attributes.TryGetValue("visibleName", out var visibleNameValue));
Assert.Equal("Visible", visibleNameValue);
Assert.False(resourceObject.Attributes.ContainsKey("secretPassword"));
Assert.False(resourceObject.Attributes.ContainsKey("internalData"));
}
Expand Down
15 changes: 8 additions & 7 deletions JsonApiToolkit.Tests/Mapping/JsonColumnMappingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,21 +133,22 @@ public void ToResourceObject_MapsJsonColumnsAsAttributes()
Assert.Equal("Test Entity", resource.Attributes["name"]);

// JSON columns should be in attributes
Assert.True(resource.Attributes.ContainsKey("jsonDataList"));
Assert.True(resource.Attributes.ContainsKey("exploitationReports"));
Assert.True(resource.Attributes.ContainsKey("complexData"));
Assert.True(resource.Attributes.TryGetValue("jsonDataList", out var jsonDataListValue));
Assert.True(
resource.Attributes.TryGetValue("exploitationReports", out var exploitationReportsValue)
);
Assert.True(resource.Attributes.TryGetValue("complexData", out var complexDataValue));

// Verify the JSON data is preserved
var jsonDataList = resource.Attributes["jsonDataList"] as List<JsonData>;
var jsonDataList = jsonDataListValue as List<JsonData>;
Assert.NotNull(jsonDataList);
Assert.Equal(2, jsonDataList.Count);

var exploitationReports =
resource.Attributes["exploitationReports"] as ICollection<ExploitationReport>;
var exploitationReports = exploitationReportsValue as ICollection<ExploitationReport>;
Assert.NotNull(exploitationReports);
Assert.Single(exploitationReports);

var complexData = resource.Attributes["complexData"] as ComplexJsonData;
var complexData = complexDataValue as ComplexJsonData;
Assert.NotNull(complexData);
Assert.Equal("Security", complexData.Category);
Assert.Equal(2, complexData.Tags.Count);
Expand Down
8 changes: 4 additions & 4 deletions JsonApiToolkit.Tests/Parsing/JsonApiQueryParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,10 @@ public void Parse_WithSingleFieldset_ParsesCorrectly()

Assert.NotNull(parameters.Fields);
Assert.Single(parameters.Fields);
Assert.True(parameters.Fields.ContainsKey("articles"));
Assert.Equal(2, parameters.Fields["articles"].Count);
Assert.Contains("title", parameters.Fields["articles"]);
Assert.Contains("content", parameters.Fields["articles"]);
Assert.True(parameters.Fields.TryGetValue("articles", out var articlesFields));
Assert.Equal(2, articlesFields!.Count);
Assert.Contains("title", articlesFields);
Assert.Contains("content", articlesFields);
}

[Fact]
Expand Down
43 changes: 17 additions & 26 deletions JsonApiToolkit/Configuration/QueryComplexityAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,37 +125,28 @@ public static int GetMaxDepth(FilterGroup group, int currentDepth = 1)
if (group.Groups.Count == 0)
return currentDepth;

int maxChildDepth = currentDepth;
foreach (var nested in group.Groups)
{
int childDepth = GetMaxDepth(nested, currentDepth + 1);
if (childDepth > maxChildDepth)
maxChildDepth = childDepth;
}
return maxChildDepth;
return group.Groups.Select(nested => GetMaxDepth(nested, currentDepth + 1)).Max();
}

private static void ValidateFilterValueLengths(FilterGroup group, int maxLength)
{
foreach (var filter in group.Filters)
var tooLong = group.Filters.FirstOrDefault(f => f.Value?.Length > maxLength);
if (tooLong != null)
{
if (filter.Value?.Length > maxLength)
{
throw new JsonApiBadRequestException(
$"Filter value for '{filter.Field}' is {filter.Value.Length} characters, "
+ $"but maximum allowed is {maxLength}. "
+ "Reduce value length or configure a higher limit via JsonApiOptions.MaxFilterValueLength.",
JsonApiErrorCodes.QueryTooComplex,
new ErrorSource { Parameter = $"filter[{filter.Field}]" },
new Dictionary<string, object>
{
["field"] = filter.Field,
["valueLength"] = filter.Value.Length,
["limit"] = maxLength,
["configKey"] = "JsonApiOptions.MaxFilterValueLength",
}
);
}
throw new JsonApiBadRequestException(
$"Filter value for '{tooLong.Field}' is {tooLong.Value!.Length} characters, "
+ $"but maximum allowed is {maxLength}. "
+ "Reduce value length or configure a higher limit via JsonApiOptions.MaxFilterValueLength.",
JsonApiErrorCodes.QueryTooComplex,
new ErrorSource { Parameter = $"filter[{tooLong.Field}]" },
new Dictionary<string, object>
{
["field"] = tooLong.Field,
["valueLength"] = tooLong.Value!.Length,
["limit"] = maxLength,
["configKey"] = "JsonApiOptions.MaxFilterValueLength",
}
);
}

foreach (var nested in group.Groups)
Expand Down
8 changes: 4 additions & 4 deletions JsonApiToolkit/Extensions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ QueryParameters parameters
if (parameters.Filter != null)
query = query.ApplyFilters(parameters.Filter);

if (parameters.Sort?.Count > 0)
query = query.ApplySorting(parameters.Sort);
else
query = query.ApplySorting([new SortParameter { Field = "Id", IsDescending = false }]);
query =
parameters.Sort?.Count > 0
? query.ApplySorting(parameters.Sort!)
: query.ApplySorting([new SortParameter { Field = "Id", IsDescending = false }]);

if (parameters.Pagination != null)
query = query.ApplyPagination(parameters.Pagination);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,11 @@ public static class FilterExpressionBuilder
}
}

foreach (FilterGroup nestedGroup in group.Groups)
{
Expression? nestedExpr = BuildFilterExpression(
nestedGroup,
parameter,
entityType,
logger
);
if (nestedExpr != null)
expressions.Add(nestedExpr);
}
expressions.AddRange(
group
.Groups.Select(g => BuildFilterExpression(g, parameter, entityType, logger))
.OfType<Expression>()
);

if (expressions.Count == 0)
return null;
Expand All @@ -96,32 +90,23 @@ public static class FilterExpressionBuilder

if (group.LogicalOperator == LogicalOperator.Not)
{
foreach (Expression expr in expressions)
{
var notExpr = Expression.Not(expr);
combinedExpression =
combinedExpression == null
? notExpr
: Expression.OrElse(combinedExpression, notExpr);
}
combinedExpression = expressions
.Select(e => (Expression)Expression.Not(e))
.Aggregate((acc, next) => Expression.OrElse(acc, next));
}
else
{
foreach (Expression expr in expressions)
{
if (combinedExpression == null)
{
combinedExpression = expr;
}
else
{
combinedExpression = group.LogicalOperator switch
{
LogicalOperator.And => Expression.AndAlso(combinedExpression, expr),
LogicalOperator.Or => Expression.OrElse(combinedExpression, expr),
_ => Expression.AndAlso(combinedExpression, expr),
};
}
combinedExpression =
combinedExpression == null
? expr
: group.LogicalOperator switch
{
LogicalOperator.And => Expression.AndAlso(combinedExpression, expr),
LogicalOperator.Or => Expression.OrElse(combinedExpression, expr),
_ => Expression.AndAlso(combinedExpression, expr),
};
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Type propertyType
if (converted != null)
convertedValues.Add(converted);
}
catch (Exception)
catch (FormatException)
{
failedValues.Add(rawValue);
}
Expand Down
Loading
Loading