Skip to content

Commit 20b3598

Browse files
Merge branch 'main' into relationship-primary-filter
2 parents 5c90d41 + 65107d5 commit 20b3598

5 files changed

Lines changed: 62 additions & 52 deletions

File tree

JsonApiToolkit.Tests/Extensions/IncludeFilterParserTests.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -286,39 +286,6 @@ public void SeparateIncludeFilters_WithFilterOnNonIncludedRelationship_ReturnsAs
286286
Assert.Empty(includeFilters);
287287
}
288288

289-
[Fact]
290-
public void SeparateIncludeFilters_WithTooManyOrConditions_ThrowsException()
291-
{
292-
// Arrange
293-
var filters = new FilterGroup
294-
{
295-
LogicalOperator = LogicalOperator.Or,
296-
Filters = new List<FilterParameter>(),
297-
};
298-
299-
// Add 11 OR conditions (exceeds limit of 10)
300-
for (int i = 0; i < 11; i++)
301-
{
302-
filters.Filters.Add(
303-
new FilterParameter
304-
{
305-
Field = "comments.status",
306-
Operator = FilterOperator.Eq,
307-
Value = $"value{i}",
308-
}
309-
);
310-
}
311-
312-
var includePaths = new List<string> { "comments" };
313-
314-
// Act & Assert
315-
var exception = Assert.Throws<JsonApiBadRequestException>(
316-
() => IncludeFilterParser.SeparateIncludeFilters(filters, includePaths)
317-
);
318-
319-
Assert.Contains("Too many OR conditions", exception.Message);
320-
}
321-
322289
[Fact]
323290
public void SeparateIncludeFilters_WithTooDeepNesting_ThrowsException()
324291
{

JsonApiToolkit.Tests/Filters/JsonApiExceptionFilterTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,29 @@ public void OnException_WithUnhandledException_LogsWithStackTrace()
213213
Times.Once
214214
);
215215
}
216+
217+
[Theory]
218+
[InlineData(402, "Payment Required")]
219+
[InlineData(410, "Gone")]
220+
[InlineData(422, "Unprocessable Entity")]
221+
[InlineData(451, "Unavailable For Legal Reasons")]
222+
public void OnException_WithJsonApiHttpException_ReturnsCorrectStatusAndTitle(
223+
int statusCode,
224+
string expectedTitle
225+
)
226+
{
227+
var exception = new JsonApiHttpException(statusCode, "Test message");
228+
var context = CreateExceptionContext(exception);
229+
230+
_filter.OnException(context);
231+
232+
Assert.True(context.ExceptionHandled);
233+
var result = Assert.IsType<ObjectResult>(context.Result);
234+
Assert.Equal(statusCode, result.StatusCode);
235+
var errorResponse = Assert.IsType<JsonApiErrorResponse>(result.Value);
236+
237+
Assert.Single(errorResponse.Errors);
238+
Assert.Equal(statusCode.ToString(), errorResponse.Errors[0].Status);
239+
Assert.Equal(expectedTitle, errorResponse.Errors[0].Title);
240+
}
216241
}

JsonApiToolkit/Extensions/Querying/Filtering/IncludeFilterParser.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ public static class IncludeFilterParser
1010
{
1111
private const int MaxIncludeFilterDepth = 3;
1212
private const int MaxIncludeFilters = 20;
13-
private const int MaxOrConditions = 10;
1413

1514
/// <summary>
1615
/// Separates main filters from include filters.
@@ -59,14 +58,6 @@ Dictionary<string, FilterGroup> filtersByRelationship
5958
{
6059
var newGroup = new FilterGroup { LogicalOperator = group.LogicalOperator };
6160

62-
// Check OR conditions count
63-
if (group.LogicalOperator == LogicalOperator.Or && group.Filters.Count > MaxOrConditions)
64-
{
65-
throw new JsonApiBadRequestException(
66-
$"Too many OR conditions in filter group. Maximum allowed: {MaxOrConditions}"
67-
);
68-
}
69-
7061
// Track filters for each relationship in this group
7162
var localIncludeFilters = new Dictionary<string, FilterGroup>(
7263
StringComparer.OrdinalIgnoreCase

JsonApiToolkit/Filters/JsonApiExceptionFilter.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Net;
2+
using System.Text.RegularExpressions;
13
using JsonApiToolkit.Models.Errors;
24
using Microsoft.AspNetCore.Mvc;
35
using Microsoft.AspNetCore.Mvc.Filters;
@@ -68,15 +70,14 @@ public void OnException(ExceptionContext context)
6870
context.ExceptionHandled = true;
6971
}
7072

71-
private static string GetTitleForStatusCode(int statusCode) =>
72-
statusCode switch
73+
private static string GetTitleForStatusCode(int statusCode)
74+
{
75+
if (Enum.IsDefined(typeof(HttpStatusCode), statusCode))
7376
{
74-
400 => "Bad Request",
75-
401 => "Unauthorized",
76-
403 => "Forbidden",
77-
404 => "Not Found",
78-
409 => "Conflict",
79-
429 => "Too Many Requests",
80-
_ => "Error",
81-
};
77+
// Convert PascalCase enum name to Title Case with spaces
78+
var name = ((HttpStatusCode)statusCode).ToString();
79+
return Regex.Replace(name, "(\\B[A-Z])", " $1");
80+
}
81+
return "Error";
82+
}
8283
}

JsonApiToolkit/Models/Errors/JsonApiErrorTypes.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,29 @@ public JsonApiTooManyRequestsException(
188188
)
189189
: base(429, message, code, errorSource, meta, innerException) { }
190190
}
191+
192+
/// <summary>
193+
/// General-purpose exception for any HTTP status code.
194+
/// Use this when the specific exception types don't cover your use case.
195+
/// </summary>
196+
public class JsonApiHttpException : JsonApiException
197+
{
198+
/// <summary>
199+
/// Initializes a new JSON:API HTTP exception with any status code.
200+
/// </summary>
201+
public JsonApiHttpException(int statusCode, string message)
202+
: base(statusCode, message) { }
203+
204+
/// <summary>
205+
/// Initializes a new JSON:API HTTP exception with additional details.
206+
/// </summary>
207+
public JsonApiHttpException(
208+
int statusCode,
209+
string message,
210+
string? code = null,
211+
ErrorSource? errorSource = null,
212+
Dictionary<string, object>? meta = null,
213+
Exception? innerException = null
214+
)
215+
: base(statusCode, message, code, errorSource, meta, innerException) { }
216+
}

0 commit comments

Comments
 (0)